How to URL encode a dictionary in C# with bracket notation - c#

Let's say I have the following class:
public class TestClass
{
public Dictionary<string, string> Property1 { get; set; }
}
If I do the following:
var dictionary = new Dictionary<string, string>();
dictionary.add("key1", "value1");
dictionary.add("key2", "value2");
var newClass = new TestClass();
newClass.Property1 = dictionary;
I am trying to URL encode this dictionary to the following:
https://baseaddress.com/resource/?Property1[key1]=value1&Property1[key2]=value2
When attempting to URL encode the dictionary via HttpUtility it is returning the ToString() method of the Dictionary which comes out as:
System.Collections.Generic.Dictionary`2[System.String,System.String]
I am trying to pass this dictionary to a .netcore API that binds to a similar Dictionary<string, string>
Edit
I was able to get it working by using the following code:
var builder = new UriBuilder(uri);
var query = HttpUtility.ParseQueryString(string.Empty);
foreach (var propInfo in obj.GetType().GetProperties())
{
var propName = propInfo.Name;
var propValue = propInfo.GetValue(obj);
if (propValue != null)
{
var dict = propValue as IDictionary;
if (dict != null)
{
foreach (var key in dict.Keys)
{
var keyName = key;
var keyValue = dict[key];
query.Add($"{propName}[{keyName}]", keyValue.ToString());
}
}
else
{
query.Add(propName, propValue.ToString());
}
}
}
builder.Query = query.ToString();
return builder.Uri;
I was hoping there was a more efficient way to make this work.

If you want to get the standard format and avoid any problem with your QueryString you can "leverage" .net core approach which indeed is a way larger than the old approach. With that said here is what you can do:
One thing....Notice that they are strings so you can add your brackets :)
Dictionary<String,StringValues>() queryString = QueryHelpers.ParseQuery("?param1=value");
StringValues secondValue=StringValues.Concat(queryString["param2"], "my other value");
parsedQueryString["yourkey"] = secondValue;
//At this point you can start concatenating as many time as needed.
QueryString.Create(parsedQueryString).ToString();
// creates the following string "?param1=value&param2=my%20other%20value"
A plus :)
// Getting a param value
var param2Value = queryString["param2"];
param2Value.ToString(); // Get the values concatenated together
param2Value.ToArray(); // Gets an array of strings
// Modifying a parameter
queryString["param1"] = "another value";
// NOTE, if there were two values, this overwrites both and leaves a single value

Related

Inline string Json to formatted Json with C#

I have to send a variables Json to Mailgun but it only accepts the curly braces format when using multi level json files. So,
How can I pass from this:
{ "vehicle.type": "car"}
To this, with C#
{"vehicle": {"type": "car"}}
Having into consideration that sometimes it could be up to 3 nested elements. Like element1.element2.element3: value
Here is what I recommend.
note: I am using the Newtonsoft.Json library available via Nuget, if you are using .NET Core, you can use the built in System.Text.Json library.
Because we have multiple properties in the object with flattened property keys, qualified with .s and we need to convert these properties into a hierarchical, nested JSON structure, merging siblings appropriately at each level, a simple string replacement is neither safe nor effective.
Therefore, the approach here will be to parse the flattened property keys, such as "hospital.hospitalExtraData1.Street" recursively inferring and creating a hierarchy of nested objects.
Let's begin
var originalJson = #"{
""hospital.Name"": ""BestOneEver"",
""hospital.Estatus"": ""Active"",
""hospital.hospitalExtraData1.Street"": ""43"",
""hospital.hospitalExtraData1.Color"": ""Blue"",
""hospital.hospitalExtraData1.hospitalExtraData2.IsExpensive"": ""No"",
""hospital.hospitalExtraData1.hospitalExtraData2.Works24Hrs"": ""Yes"",
""patient.Name"": ""Leonel Messi"",
""patient.Age"": ""23""
}";
var original = JsonConvert.DeserializeObject<IDictionary<string, object>>(originalJson);
Now we have an object model we can work with and restructure.
We will do this using recursion
var original = JsonConvert.DeserializeObject<IDictionary<string, object>>(originalJson);
IDictionary<string, object> Expand(IDictionary<string, object> input)
{
var result = new Dictionary<string, object>();
foreach (var property in input)
{
var (key, remainder) = ParseKey(property.Key);
if (!result.ContainsKey(key))
{
result[key] = remainder != null
? Expand(new Dictionary<string, object>
{
[remainder] = property.Value
})
: property.Value;
}
else if (result[key] is IDictionary<string, object> inner)
{
inner[remainder] = property.Value;
result[key] = Expand(inner);
}
else
{
result[key] = property.Value;
}
}
return result;
}
(string key, string remainder) ParseKey(string key)
{
var dotIndex = key.IndexOf('.');
if (dotIndex != -1)
{
return (key.Substring(0, dotIndex), key.Substring(dotIndex + 1));
}
return (key, null);
}
var expanded = Expand(original);
var expandedJson = JsonConvert.SerializeObject(expanded, Newtonsoft.Json.Formatting.Indented);
Result:
{
"hospital": {
"Name": "BestOneEver",
"Estatus": "Active",
"hospitalExtraData1": {
"Street": "43",
"Color": "Blue",
"hospitalExtraData2": {
"IsExpensive": "No",
"Works24Hrs": "Yes"
}
}
},
"patient": {
"Name": "Leonel Messi",
"Age": "23"
}
}
Here is tricky way using string replacement.
replace for any dot(.) with this (": {") and add close tag (}) at the end for every dot(.) count. Good luck!
Try This:
IDictionary<string, object> Expand(IDictionary<string, object> d)
{
var result = new Dictionary<string, object>();
foreach (KeyValuePair<string, object> item in d)
{
var segments = item.Key.Split('.');
if (segments.Length > 1)
{
if (result.ContainsKey(segments[0]))
{
dynamic obj = new ExpandoObject();
obj = result[segments[0]];
IDictionary<string, object> myObj = obj;
myObj.Add(string.Join(".", segments.Skip(1)), item.Value);
result[segments[0]] = Expand(myObj);
}
else
{
result[segments[0]] = Expand(new Dictionary<string, object>
{
[string.Join(".", segments.Skip(1))] = item.Value
});
}
}
else
{
result[segments[0]] = item.Value;
}
}
return result;
}

Sensible way to output content of dictionary as valid html tags?

I am trying to generate hreflang tags like so:
<link hreflang="en-DE" rel="alternate" href="/en-DE" />
I currently am putting together my dictionary as below, and I thought I could use a 2nd foreach loop to generate the tags with the key value pairs:
public static IHtmlString HrefLangLinks(this PageData currentPage)
{
var hrefLangTags = string.Empty;
var availablePageLanguages = currentPage.ExistingLanguages.Select(culture => culture.Name).ToArray();
Dictionary<string, string> langs = new Dictionary<string, string>();
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
foreach (string cultureName in availablePageLanguages)
{
var culturePage = contentLoader.Get<PageData>(currentPage.ContentGuid, new LanguageSelector(cultureName));
var culturePath = urlResolver.GetVirtualPath(culturePage.ContentLink, culturePage.Language.Name);
langs.Add(cultureName, culturePath.GetUrl());
foreach (KeyValuePair<string, string> entry in langs)
{
hrefLangTags += ("<link hreflang=\"{0}\" rel=\"alternate\" href=\"{1}\" >", langs.Keys, langs.Values);
}
}
return new HtmlString(hrefLangTags);
}
Is there a simple and elegant way to iterate through the dictionary and create my tags?
Why are you even using a dictionay? You could just iterate through "availablePageLanguages" and append the needed data to your "hrefLangTags".
So like this:
public static IHtmlString HrefLangLinks(this PageData currentPage)
{
var hrefLangTags = string.Empty;
var availablePageLanguages = currentPage.ExistingLanguages.Select(culture => culture.Name).ToArray();
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
foreach (string cultureName in availablePageLanguages)
{
var culturePage = contentLoader.Get<PageData>(currentPage.ContentGuid, new LanguageSelector(cultureName));
var culturePath = urlResolver.GetVirtualPath(culturePage.ContentLink, culturePage.Language.Name);
hrefLangTags += String.Format("<link hreflang=\"{0}\" rel=\"alternate\" href=\"{1}\" >", culturName, culturePath.GetUrl());
}
return new HtmlString(hrefLangTags);
}
Your method does not make use of the first principle of SOLID (Single responsibility). You want to return links based on data input. But you are generating the needed data in the same method that is responsible for converting it to a list of IHtmlStrings.
The code beneath is may not be compact but it's a bit easier to read because the nested foreach loop is removed. You could call the method below by chaining them like: CreateLanguageLinksList(CreateWorkingLanguageDictionary(currentPage)) or do it like so
var languageDictionary = CreateWorkingLanguageDictionary(currentPage);
var listOfIHtmlStrings = CreateLanguageLinksList(languageDictionary);
The code above is easier to read, so the cognetive load is less straining on the mind.
public Dictionary<string, string> CreateWorkingLanguageDictionary(this PageData currentPage)
{
var languageDictionary = new Dictionary<string, string>();
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
foreach (string cultureName in availablePageLanguages)
{
var culturePage = contentLoader.Get<PageData>(currentPage.ContentGuid, new LanguageSelector(cultureName));
var culturePath = urlResolver.GetVirtualPath(culturePage.ContentLink, culturePage.Language.Name);
languageDictionary.Add(cultureName, culturePath.GetUrl());
}
return languageDictionary;
}
public IList<IHtmlString> CreateLanguageLinksList(Dictionary<string, string> langs)
{
var htmlStringList = new List<IHtmlString>();
foreach (KeyValuePair<string, string> entry in langs)
{
htmlStringList.add(new HtmlString("<link hreflang=\"{0}\" rel=\"alternate\" href=\"{1}\" >", langs.Keys, langs.Values));
}
return htmlStringList;
}
Just for fun, you could do it in linq, it's a little more succinct:
public static IHtmlString HrefLangLinks(this PageData currentPage)
{
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
var links = currentPage.ExistingLanguages.Select(culture =>
{
var culturePage = contentLoader.Get<PageData>(currentPage.ContentGuid, new LanguageSelector(culture.Name));
var culturePath = urlResolver.GetVirtualPath(culturePage.ContentLink, culturePage.Language.Name);
return $"<link hreflang=\"{culture.Name}\" rel=\"alternate\" href=\"{culturePath.GetUrl()}\" >";
});
return new HtmlString(string.Join("",links));
}
Disclaimer: This is totally untested or verified.

Cannot convert type 'Newtonsoft.Json.Linq.JProperty' to 'System.Collections.Generic.KeyValuePair<string,object>'

var stringResult = {"reporting":{"default":{"Outpatient":8045376.0,"OutpatientPMPM":101.6472,"totalWorkersCompClaimsPaid":7718428.46,"totalWorkersCompClaimsPaidPMPM":97.5165,"totalMedicalPaidAmount":1.6883294E7,"totalMedicalPaidAmountPMPM":213.3076,"totalVisionClaimsPaid":2837.69,"totalVisionClaimsPaidPMPM":0.0359,"totalPharmacyPaidAmount":2.478251486E7,"totalPharmacyPaidAmountPMPM":313.1082,"totalDentalClaimsPaid":12271.67,"totalDentalClaimsPaidPMPM":0.155,"employeeMonths":35415.0,"memberMonths":79150.0,"subscribers":3009.0,"totalVendorPaidAmount":97034.0,"totalVendorPaidAmountPMPM":1.226,"Office":4285314.0,"OfficePMPM":54.1417,"Inpatient":4552604.0,"InpatientPMPM":57.5187,"members":6741.0,"totalHealthPlanClaimsPaid":4.949638068000001E7,"averageFamilySize":2.2403,"totalHealthPlanClaimsPaidPMPM":625.3492}}}
dynamic json = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(stringResult);
float value = json.reporting.#default.totalPharmacyPaidAmount;
foreach (KeyValuePair<string, dynamic> page in json.reporting.#default)
{
var key = page.Key;
var totalHealthPlanClaimsPaid = page.Value.totalHealthPlanClaimsPaid;
var averageFamilySize = page.Value.averageFamilySize;
}
I am getting an error in foreach loop as
Cannot convert type 'Newtonsoft.Json.Linq.JProperty' to
'System.Collections.Generic.KeyValuePair'.
while performing the above task where I am doing wrong.
json.reporting.#default returns Newtonsoft.Json.Linq.JProperty, change your code:
foreach (KeyValuePair<string, dynamic> page in json.reporting.#default.Children())
{
var key = page.Key;
var totalHealthPlanClaimsPaid = page.Value.totalHealthPlanClaimsPaid;
var averageFamilySize = page.Value.averageFamilySize;
}
docs for read about it http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Linq_JProperty.htm
What #progpow said is right. You can also convert the result set into a dictionary and iterate through it.
Dictionary<string, dynamic> result = json.reporting.#default.ToObject<Dictionary<string,dynamic>>();
foreach (KeyValuePair<string,dynamic> page in result)
{
var key = page.Key;
var totalHealthPlanClaimsPaid = page.Value.totalHealthPlanClaimsPaid;
var averageFamilySize = page.Value.averageFamilySize;
}

Parse String with Regex c#

I am trying to customise a DevExpress grid filter.
Currently I return the data from my api, and so I need to parse the built in string which is returned from the grid.
An example of the filter string is;
StartsWith([name], 'test') And StartsWith([quantity], '12') And
StartsWith([id], '1') And StartsWith([date], '01/10/2015')
I would like to convert this to a Dictionary in the most efficient way?
You could use Regex for filtering the key/value pair outta your string and a simple foreach to prepare the dictionary.
This could be a solution:
public static Dictionary<string, object> FilterAPIData(string data)
{
var r = new Regex(#"\[\w+\], \'[\w/]+\'");
var result = r.Matches(data);
var dict = new Dictionary<string, object>();
foreach (Match item in result)
{
var val = item.Value.Split(',');
dict.Add(val[0], val[1]);
}
return dict;
}
Regex might be the best option for this, but I'll show you how to do it without Regex as it can be a bit difficult to understand.
Assuming your string will always be in this format you can do this:
string str = "StartsWith([name], 'test') And StartsWith([quantity], '12') And StartsWith([id], '1') And StartsWith([date], '01/10/2015')";
var strArray = str.Split(new string[]{"And "}, StringSplitOptions.None);
var dict = new Dictionary<string, string>();
foreach(var value in strArray)
{
dict.Add(GetStringBetween(value, "[", "]"), GetStringBetween(value, "'", "'"));
}
private string GetStringBetween(string value, string startDelim, string endDelim)
{
int first = value.IndexOf(startDelim) + startDelim.Length;
int last = value.LastIndexOf(endDelim);
return value.Substring(first, last - first);
}
//Output :
//name test
//quantity 12
//id 1
// date 01/10/2015
If there other formats the string can be in you can adjust as needed. I would also consider adding in more validation/error handling, but I will let you figure that out ;)

How can I populate a NameValueCollection via a parameter in a shared method?

I have a code segment:
var requestMock = new Mock<HttpRequestBase>();
var queryString = new NameValueCollection();
queryString["abc"] = "123";
queryString["qwe"] = "456";
queryString["yui"] = "678";
...
requestMock.SetupGet(rqst => rqst.QueryString).Returns(queryString);
Now, I would like to have the above segment written as a method:
var requestMock = GetRequestMock(???);
I intend to send the query string key/values which can be anything.
And the count of k/v pairs also can be anything.
public Mock<HttpRequestBase> GetRequestMock(???)
{
var requestMock = new Mock<HttpRequestBase>();
....
requestMock.SetupGet(rqst => rqst.QueryString).Returns(queryString);
return requestMock;
}
What would be the best way to do this eficiently and simply?
One way would be to use a Dictionary:
public Mock<HttpRequestBase> GetRequestMock(Dictionary<string, object> queryParms)
{
var queryString = new NameValueCollection();
foreach (KeyValuePair<string, object> kvp in queryParms)
{
queryString[kvp.Key] = Convert.ToString(kvp.Value);
}
...
}
and then you can call it like this:
GetRequestMock(new Dictionary<string, object> { { "abc", "123" }, { "qwe", "456" } } );

Categories