How can I get the value of a specific nested property, from a JSON object, at runtime?
Here is an example that ilustrates what I am trying to do (without error checking, because it's just a quick example):
JSON Object:
string jsonobj = "{
"Header":"someHeader",
"FirstNest":{
"Property1":"123",
"property2":"321",
"SecondNest":{
"property3":"456",
"property4":"789"
}
}
In my code, I would have something like this :
string read = Console.ReadLine();
where the user will input, for example FirstNest.Property1 and then I would return 123. I could do something like this:
JObject msg = JObject.Parse(jsonobj);
string[] tosearch = read.Split('.'); // This would give me FirstNest Property1
string tofind = (string)msg[tosearch[0]][tosearch[1]];
The problem arises when the user wants to access FirstNest.SecondNest.property3, because my hard-coded method can only take two strings. How can I build such a "query" method at runtime, with which I could enable the user to search for any property?
One soultion would be to make a function
GetProperty(JObject msg, string str1, string str2)
{
return (string)msg[str1][str2];
}
then another one , which would take 3 strings as input :
GetProperty(JObject msg, string str1, string str2, string str3)
{
return (string)msg[str1][str2][str3];
}
, then another one, which would take 4 strings as input, and so on... which doesn't seem like an efficient solution.
How about this (exception handling excluded for brevity):
public string GetJsonPropertyValue(string json, string query)
{
JToken token = JObject.Parse(json);
foreach(string queryComponent in query.Split('.'))
{
token = token[queryComponent];
}
return token.ToString();
}
Given the json in your example a search for this FirstNest.SecondNest.property3 will return 456. You should include any necessary user input validation and exception handling.
Related
I'm beginer in C#. Now I have next task: In method I get template and arguments and I have to return formated string.
For example:
template = "Hello, {name}!"
name = "Bob"
So result must be a string -> Hello, Bob!
public static string GetHelloGreeting(string template, string name)
{
return string.Format(template, name);
}
String.Format expects an index in the braces. You want to pass the name in it, so you can replace it with the actual name value.
I'd suggest to use String.Replace:
public static string GetHelloGreeting(string template, string name)
{
return template.Replace("{name}", name);
}
You could provide a method which is more reusable. For example:
public static string ReplaceAll(string template, params (string key, string value)[] replacements)
{
foreach (var kv in replacements)
{
template = template.Replace("{"+ kv.key + "}", kv.value);
}
return template;
}
Your example:
string res = ReplaceAll("Hello, {name}!", ("name", "Bob"));
but also possible with multiple parameters:
string res = ReplaceAll("Hello, {name}! Now it's {time}", ("name", "Bob"), ("time", DateTime.Now.ToString("HH:mm")));
The value of your template parameter will have to change somehow. If you want to use string interpolation, this answer shows that. So
template = $"Hello, {name}"; in which case you wouldn't need to use String.Format at all. Just make sure you define name before you define template.
Or you could use String.Format(template, name); like you have but you would need template = "Hello, {0}!";
The 0 is the index of the variable that will go in that position. For more info see String.Format
when specifying a format you use an index for the parameters that will follow. It is called a composite format string:
string template = "Hello, {0}!"
this makes it independent of variable names. But the true reason is that the overload of the Format method that you are using takes a params array as parameter as you can see in the method signature:
public static string Format (string format, params object?[] args);
so the index that is found in the template will be applied to extract the objects on the appropriate places from the array of objects that you pass into the method
If you want to use string.Format(), the template must be correct. Add the character $ at the beginning of the template:
try this:
string name = "Bob";
string template = $"Hello, {name}!";
Console.WriteLine(GetHelloGreeting(template, name)); // Hello, Bob!
public static string GetHelloGreeting(string template, string name)
{
return string.Format(template, name);
}
reult:
Hello, Bob!
I'm trying to create a string literal representing an array of JSON objects so I thought of using string interpolation feature as shown in the code below:
public static void MyMethod(string abc, int pqr)
{
string p = $"[{{\"Key\":\"{abc}\",\"Value\": {pqr} }}]";
}
Now I thought of using verbatim string so that I don't have to escape double quotes using backslashes. So I came to know through this answer that verbatim string and string interpolation can be used together. So I changed my code as below:
public static void MyMethod(string abc, int pqr)
{
string p = $#"[{{"Key":"{abc}","Value": {pqr} }}]";
}
But it fails to compile. Can anyone help me if there is anything wrong in my usage or it will not be possible to escape double quotes in such a case using string verbatim feature of C#?
The best way is to use JSON serializers as they have in-built handling related to escape characters and other things. See here.
However, if we want to go through this path only to create the JSON string manually, then it can be solved as follows by changing the inner double quotes to single quotes :
public static string MyMethod(string abc, int pqr)
{
string p = $#"[{{'Key':'{ abc}','Value': {pqr} }}]";
return p;
}
I agree with everyone else that building it from strings is a bad idea.
I also understand that you don't want to include an extra dependency.
Here's a bit of code I wrote previously to convert a Dictionary to a JSON string. It's pretty basic, only accepts string types, and doesn't escape quote marks in any of the names/values, but that can be done fairly easily.
If you're trying to serialize a large JSON string from basic types, this is the way I'd recommend to do it. It'll help you stay sane.
private static string DictToJson(Dictionary<string, string> Dict)
{
var json = new StringBuilder();
foreach (var Key in Dict.Keys)
{
if (json.Length != 0)
json = json.Append(",\n");
json.AppendFormat("\"{0}\" : \"{1}\"", Key, Dict[Key]);
}
return "{" + json.ToString() + "}";
}
you can create dictionary and serialize it to json using Json.NET does this.
Dictionary<string, string> values = new Dictionary<string, string>();
values.Add("key1", "value1");
values.Add("key2", "value2");
string json = JsonConvert.SerializeObject(values);
// {
// "key1": "value1",
// "key2": "value2"
// }
you can see here more detail : http://www.newtonsoft.com/json/help/html/SerializingCollections.htm
I am trying to use json.net to produce a web service but I am a bit perplexed as to why < string > is showing in the dummy data
[WebMethod]
public string getCustomerInfo(string IVACode)
{
var customerData = _dal.apertureAppstblCustomers.Where(a => a.IVACode == IVACode).ToList();
var jsonstring = JsonConvert.SerializeObject(customerData);
return jsonstring;
}
ie its starting like <string> and ending </string> how do i get it to display CustomerInformationinstead of string is it good practise to be naming the nodes?.
[{"id":"7aee450a-a9a7-4f19-83d3-467a3b8a39c0","IVACode":"IVA002","FirstName":"Peter","LastName":"Carson","AddressLine1":"Waters Edge Belfast","AddressLine2":null,"Town":"Belfast","County":"Down","PostCode":"BT99YXX","Telephone":null,"EmailAddress":"email","isActive":true,"authUserName":null,"authCreatedDate":null,"personalProgressValue":null,"contributionsToDate":null,"totalContributions":null,"totalArrears":50000.00,"totalPaid":null,"isDeleted":false,"deviceId":null,"deviceOs":null}]
You will need to convert the serialized string back to your object and then consume it to show the relevant information.
Example below.
JsonConvert.DeserializeObject(jsonstring)
You shouldn't get that in the serialized string but you can replace those tokens using Replace() string function like
jsonstring = jsonstring.Replace("<string>","").Replace("</string>","");
I am confused by all the different escaping mechanisms for strings in C#. What I want is an escaping/unescaping method that:
1) Can be used on any string
2) escape+unescape is guaranteed to return the initial string
3) Replaces all punctuation with something else. If that is too much to ask, then at least commas, braces, and #. I am fine with spaces not being escaped.
4) Is unlikely to ever change.
Does it exist?
EDIT: This is for purposes of seriliazing and deserializing app-generated attributes. So my object may or may not have values for Attribute1, Attribute2, Attribute3, etc. Simplifying a bit, the idea is to do something like the below. Goal is to have the encoded collection be brief and more-or-less human-readable.
I am asking what methods would make sense to use for Escape and Unescape.
public abstract class GenericAttribute {
const string key1 = "KEY1"; //It is fine to put some restrictions on the keys, i.e. no punctuation
const string key2 = "KEY2";
public abstract string Encode(); // NO RESTRICTIONS ON WHAT ENCODE MIGHT RETURN
public static GenericAttribute FromKeyValuePair (string key, string value) {
switch (key) {
case key1: return new ConcreteAttribute1(value);
case key2: return new ConcreteAttribute2(value);
// etc.
}
}
}
public class AttributeCollection {
Dictionary <string, GenericAttribute> Content {get;set;}
public string Encode() {
string r = "";
bool first = true;
foreach (KeyValuePair<string, GenericAttribute> pair in this.Content) {
if (first) {
first = false;
} else {
r+=",";
}
r+=(pair.Key + "=" + Escape(pair.Value.Encode()));
}
return r;
}
public AttributeCollection(string encodedCollection) {
// input string is the return value of the Encode method
this.Content = new Dictionary<string, GenericAttribute>();
string[] array = encodedCollection.Split(',');
foreach(string component in array) {
int equalsIndex = component.IndexOf('=');
string key = component.Substring(0, equalsIndex);
string value = component.Substring(equalsIndex+1);
GenericAttribute attribute = GenericAttribute.FromKeyValuePair(key, Unescape(value));
this.Content[key]=attribute;
}
}
}
I'm not entirely sure what your asking, but I believe your intent is for the escaped character to be included, even with the escape.
var content = #"\'Hello";
Console.WriteLine(content);
// Output:
\'Hello
By utilizing the # it will include said escaping, making it apart of your string. That is for the server-side with C#, to account for other languages and escape formats only you would know that.
You can find some great information on C# escaping here:
MSDN Blog
Try using HttpServerUtility.UrlEncode and HttpServerUtility.UrlDecode. I think that will encode and decode all the things you want.
See the MSDN Docs and here is a description of the mapping on Wikipedia.
I am trying to create a generic formatter/parser combination.
Example scenario:
I have a string for string.Format(), e.g. var format = "{0}-{1}"
I have an array of object (string) for the input, e.g. var arr = new[] { "asdf", "qwer" }
I am formatting the array using the format string, e.g. var res = string.Format(format, arr)
What I am trying to do is to revert back the formatted string back into the array of object (string). Something like (pseudo code):
var arr2 = string.Unformat(format, res)
// when: res = "asdf-qwer"
// arr2 should be equal to arr
Anyone have experience doing something like this? I'm thinking about using regular expressions (modify the original format string, and then pass it to Regex.Matches to get the array) and run it for each placeholder in the format string. Is this feasible or is there any other more efficient solution?
While the comments about lost information are valid, sometimes you just want to get the string values of of a string with known formatting.
One method is this blog post written by a friend of mine. He implemented an extension method called string[] ParseExact(), akin to DateTime.ParseExact(). Data is returned as an array of strings, but if you can live with that, it is terribly handy.
public static class StringExtensions
{
public static string[] ParseExact(
this string data,
string format)
{
return ParseExact(data, format, false);
}
public static string[] ParseExact(
this string data,
string format,
bool ignoreCase)
{
string[] values;
if (TryParseExact(data, format, out values, ignoreCase))
return values;
else
throw new ArgumentException("Format not compatible with value.");
}
public static bool TryExtract(
this string data,
string format,
out string[] values)
{
return TryParseExact(data, format, out values, false);
}
public static bool TryParseExact(
this string data,
string format,
out string[] values,
bool ignoreCase)
{
int tokenCount = 0;
format = Regex.Escape(format).Replace("\\{", "{");
for (tokenCount = 0; ; tokenCount++)
{
string token = string.Format("{{{0}}}", tokenCount);
if (!format.Contains(token)) break;
format = format.Replace(token,
string.Format("(?'group{0}'.*)", tokenCount));
}
RegexOptions options =
ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
Match match = new Regex(format, options).Match(data);
if (tokenCount != (match.Groups.Count - 1))
{
values = new string[] { };
return false;
}
else
{
values = new string[tokenCount];
for (int index = 0; index < tokenCount; index++)
values[index] =
match.Groups[string.Format("group{0}", index)].Value;
return true;
}
}
}
You can't unformat because information is lost. String.Format is a "destructive" algorithm, which means you can't (always) go back.
Create a new class inheriting from string, where you add a member that keeps track of the "{0}-{1}" and the { "asdf", "qwer" }, override ToString(), and modify a little your code.
If it becomes too tricky, just create the same class, but not inheriting from string and modify a little more your code.
IMO, that's the best way to do this.
It's simply not possible in the generic case. Some information will be "lost" (string boundaries) in the Format method. Assume:
String.Format("{0}-{1}", "hello-world", "stack-overflow");
How would you "Unformat" it?
Assuming "-" is not in the original strings, can you not just use Split?
var arr2 = formattedString.Split('-');
Note that this only applies to the presented example with an assumption. Any reverse algorithm is dependent on the kind of formatting employed; an inverse operation may not even be possible, as noted by the other answers.
A simple solution might be to
replace all format tokens with (.*)
escape all other special charaters in format
make the regex match non-greedy
This would resolve the ambiguities to the shortest possible match.
(I'm not good at RegEx, so please correct me, folks :))
After formatting, you can put the resulting string and the array of objects into a dictionary with the string as key:
Dictionary<string,string []> unFormatLookup = new Dictionary<string,string []>
...
var arr = new string [] {"asdf", "qwer" };
var res = string.Format(format, arr);
unFormatLookup.Add(res,arr);
and in Unformat method, you can simply pass a string and look up that string and return the array used:
string [] Unformat(string res)
{
string [] arr;
unFormatLoopup.TryGetValue(res,out arr); //you can also check the return value of TryGetValue and throw an exception if the input string is not in.
return arr;
}