Query Json Object dynamically - c#

I am trying to implement a dynamic condition on a JSON Object.
After searching for a while, I am not able to find any resources which can meet my needs.
In the below fashion, I am getting the JsonObject dynamic string into jsonObject variable.
string inputFromAPI = client.GetStringAsync(URL).Result;
dynamic jsonObject = JValue.Parse(inputFromAPI);
I now have a condition which can change on need basis.For example I can have a condition which says
"inputFromAPI.DidWeCharge == true && inputFromAPI.DidWeHold == false"
The above line can change on the fly. Any inputs how to address this will be greatly appreciated.

I can't comment for clarification (don't have the rep yet), but if you need to be able to read the property name because it's changing you can do this. You would have to know the name though.
var JSONobj = JObject.Parse(json);
foreach (JToken child in JSONobj.Children())
{
var prop = child as JProperty;
var propertyName = prop.Name; // this will give you the name of the property
if (propertyName == "DidWeCharge")
{
var value = prop.Value; // Do something here with value?
}
if (propertyName == "DidWeHold")
{
var value = prop.Value; // Do something here with value?
}
var propertyType = prop.Value.Type; // this return the type as a JTokenType enum.
}
I don't know how nested your JSON is, so you may have to traverse further down with another foreach on the child by doing child.Children().

You can use ExpandoObject:
var expandoObj = JsonConvert.DeserializeObject<ExpandoObject>(jsonObject);
expandoObj.yourProperty
JsonConvert is from Newtonsoft.Json package.

You may be able to use Jpath:
using Newtonsoft.Json -
....
var json = #"
{
stuff : [
{
value : 1
},
{
value : 2
}
]
}";
var token = JToken.Parse(json);
var something = token.SelectTokens("$.stuff[?(#.value == 1)]").ToList();

Related

C# find JSON value based only on Key name through multiple levels of array

I have a variety of input JSON formatted data which all contain a particular key-name terminalSize. This is the only piece I know. The total number of JSON trees or the exact depth of terminalSize inside the JSON tree will forever be an unknown and subject to change.
I'm looking for a C# solution to loop through every child of the JSON string and find terminalSize then fetch the value.
I've tried this with success but it will only work if terminalSize is in the first level of the JSON:
var list = JsonConvert.DeserializeObject<List<Dictionary<string, string>>>(jsonString);
var dict = list.SelectMany(d => d).ToDictionary(p => p.Key, p => p.Value);
var terminal = dict["terminalSize"];
Example 1.
{
"status": "success",
"data": {
"terminalSize": 3766505.46,
"totalTerminalSize": 3766505.46
},
"message": null
}
Example 2.
{
"lastUpdated": 1588020678,
"terminalData": {
"terminalSize": "451679852",
"totalTerminalSize": "2100000000"
},
"terminalValueSeries": {
"8x7": 2.33,
"8x6": 3.73,
"8x5": 4.49,
"8x4": 3.68,
"8x3": 13998,
"8x2": 274936,
"8x1": 5.09
}
}
Example 3.
{
"terminalSize": "492612346.17",
"terminalStatus": "online"
}
You can parse your JSON to a JToken, then use SelectToken with the recursive descent JsonPath operator .. to get the terminalSize anywhere in the JSON:
var terminalSize = (double?) JToken.Parse(json).SelectToken("$..terminalSize");
Fiddle: https://dotnetfiddle.net/5ziYbP
If there might be multiple terminalSize keys in the JSON, for example if you had an array of terminals, you can use SelectTokens instead and put the terminal sizes into a Dictionary keyed by path:
var sizes = JToken.Parse(json4)
.SelectTokens("$..terminalSize")
.ToDictionary(t => t.Path, t => (double)t);
Fiddle: https://dotnetfiddle.net/ivSM88
You could also use linq and filter the JProperty collection based on JProperty.Name. For example
var result = JObject.Parse(jsonString)
.DescendantsAndSelf()
.OfType<JProperty>()
.Single(x=>x.Name.Equals("terminalSize"))
.Value;
You may parse your JSON into JObject, then recursively go through all properties and sub objects to find a terminalSize value. There is no need to deserialize the entire JSON into specific object
var json = JObject.Parse(jsonString);
var result = GetTerminalSize(json);
double GetTerminalSize(JObject input)
{
foreach (var property in input.Properties())
{
if (property.Name == "terminalSize")
return property.Value.Value<double>();
if (property.Value.Type == JTokenType.Object)
return GetTerminalSize((JObject) property.Value);
//not sure, if the is a need to handle an array
if (property.Value.Type == JTokenType.Array)
foreach (var item in (JArray) property.Value)
return GetTerminalSize((JObject) item);
}
return 0;
}
It returns a correct value for all 3 examples

Unable to update JToken value

I'm trying to update the value of JToken but its reference is not getting updated.
JSON string:
{
"Title": "master",
"Presentation": [
{
"Component": {
"Content": {
"Title": "Set New Title",
}
}
}
]
}
and the usage is given below
JObject jo = JObject.Parse(File.ReadAllText(file.json));
foreach (var token in jo.SelectTokens("$..Component.Content").Children())
{
JProperty prop = token.ToObject<JProperty>();
prop.Value = "New Title";
}
string jsonText = JsonConvert.SerializeObject(jo, Formatting.Indented);
In this example, I'm trying to update the value of Title property. It is getting updated within foreach, means local variable is getting updated but changes are not reflecting in main jobject.
Can anyone help me if i'm doing anything wrong?
Once you call ToObject then you are working with a copy.
If instead you try this, it should work:
JObject jo = JObject.Parse(File.ReadAllText(file.json));
foreach (var prop in jo.SelectTokens("$..Component.Content")
.Children().OfType<JProperty>())
{
prop.Value = "New Title";
}
string jsonText = JsonConvert.SerializeObject(jo, Formatting.Indented);
or to handle multiple types of JTokens:
JObject jo = JObject.Parse(File.ReadAllText(file.json));
foreach (var token in jo.SelectTokens("$..Component.Content")
.Children())
{
var prop = token as JProperty;
if (prop != null) prop.Value = "New Title";
var array = token as JArray;
if (array != null)
{
// some other logic
}
}
string jsonText = JsonConvert.SerializeObject(jo, Formatting.Indented);
The answer from Stuart may be erroneous because "Content" may contain other children and all of theme could be renamed or their values could be changed.
I've encountered the similar issue.
From the body I needed to remove value, because it was too long for logging and unnecesary, so I needed to change it's value.
I could've changed it with indexer like token["name"], but "name" could be of different cases, so I needed an universal case independent way to erase it's value:
And I implemented it other way:
var jObject = JObject.Parse(body);
JToken token;
if (jObject.TryGetValue(
Constants.FieldName,
StringComparison.InvariantCultureIgnoreCase,
out token))
{
var jProperty = token.Parent as JProperty;
if (jProperty != null)
{
jProperty.Value = "removed";
}
body = jObject.ToString(Formatting.Indented);
}
Here you have a magnificient example of how you can do this, in a proper way. Both solutions above didn't work for me, but this very simple yes. Simply work with the JToken or Object as an array. That's all.
https://www.newtonsoft.com/json/help/html/ModifyJson.htm

Change the property names in a JSON object before conversion to XML

I am trying to convert some JSON to XML, but before that I need to change some properties to make a successful conversion.
Some of the properties in the JSON structure start with numbers, and when I try to make the conversion to XML I get an error because XML does not admit tags that start with numbers.
So, one solution that works for me is to change those property names that start with numbers by adding a prefix to the property.
I have been trying to do something like this:
public string ChangeNumericalPropertyNames(JsonReader reader)
{
JObject jo = JObject.Load(reader);
foreach (JProperty jp in jo.Properties())
{
if (Regex.IsMatch(jp.Name, #"^\d"))
{
string name = "n" + jp.Name;
//Logic to set changed name
}
}
return "Here I want to return the entire json string with changed names";
}
When I try this:
jp.Name = name;
Visual studio says that is not possible because jp.Name is read only.
Does anybody know how to achieve this solution?
Since the property name is read only, you'll need to replace the whole property. You can use the Replace method to do this:
if (Regex.IsMatch(jp.Name, #"^\d"))
{
string name = "n" + jp.Name;
jp.Replace(new JProperty(name, jp.Value));
}
However, this will lead to another problem-- since you are trying to modify the Properties collection of the JObject while iterating over it, Json.Net will throw an InvalidOperationException. To get around this, you must copy the the properties to a separate list and iterate over that instead. You can do that using the ToList() method in your foreach like this:
foreach (JProperty jp in jo.Properties().ToList())
Finally, to convert the updated JObject back to JSON, just use ToString(). Putting it all together we have:
public static string ChangeNumericalPropertyNames(JsonReader reader)
{
JObject jo = JObject.Load(reader);
foreach (JProperty jp in jo.Properties().ToList())
{
if (Regex.IsMatch(jp.Name, #"^\d"))
{
string name = "n" + jp.Name;
jp.Replace(new JProperty(name, jp.Value));
}
}
return jo.ToString();
}
Fiddle: https://dotnetfiddle.net/rX4Jyy
The above method will only handle a simple JSON object with properties all on one level. You indicated in your comment that your actual JSON is not flat, but hierarchical. In order to replace all of the numeric property names in a hierarchical structure, you'll need to make your method recursive, like this:
public static string ChangeNumericalPropertyNames(JsonReader reader)
{
JObject jo = JObject.Load(reader);
ChangeNumericalPropertyNames(jo);
return jo.ToString();
}
public static void ChangeNumericalPropertyNames(JObject jo)
{
foreach (JProperty jp in jo.Properties().ToList())
{
if (jp.Value.Type == JTokenType.Object)
{
ChangeNumericalPropertyNames((JObject)jp.Value);
}
else if (jp.Value.Type == JTokenType.Array)
{
foreach (JToken child in jp.Value)
{
if (child.Type == JTokenType.Object)
{
ChangeNumericalPropertyNames((JObject)child);
}
}
}
if (Regex.IsMatch(jp.Name, #"^\d"))
{
string name = "n" + jp.Name;
jp.Replace(new JProperty(name, jp.Value));
}
}
}
Fiddle: https://dotnetfiddle.net/qeZK1C

Getting property/object-name from JSON-List in C#

I have the following JSON-String:
{"object":{"4711":{"type":"volume","owner":"john doe","time":1426156658,"description":"Jodel"},"0815":{"type":"fax","owner":"John Doe","time":1422900028,"description":"","page_count":1,"status":"ok","tag":["342ced30-7c34-11e3-ad00-00259073fd04","342ced33-7c34-11e3-ad00-00259073fd04"]}},"status":"ok"}
A human readable screenshot of that data:
I want to get the Values "4711" and "0815" of that data. I iterate through the data with the following code:
JObject tags = GetJsonResponse();
var objectContainer = tags.GetValue("object");
if (objectContainer != null) {
foreach (var tag in objectContainer) {
var property=tag.HowToGetThatMagicProperty();
}
}
At the position "var property=" I want to get the values "4711".
I could just use String-Manipulation
string tagName = tag.ToString().Split(':')[0].Replace("\"", string.Empty);
but there must be a better, more OOP-like way
If you get the "object" object as a JObject explicitly, you can access the Key property on each member inside of the JObject. Currently objectContainer is a JToken, which isn't specific enough:
JObject objectContainer = tags.Value<JObject>("object");
foreach (KeyValuePair<string, JToken> tag in objectContainer)
{
var property = tag.Key;
Console.WriteLine (property); // 4711, etc.
}
JObject exposes an implementation of IEnumerable.GetEnumerator that returns KeyValuePair<string, JToken>s containing the name and value of each property in the object.
Example: https://dotnetfiddle.net/QbK6MU
I got the results using this
foreach (var tag in objectContainer)
{
var property = tag.Path.Substring(tag.Path.IndexOf(".") + 1);
Console.WriteLine(property);
}
}
Console.ReadLine();
}

How to get children data from json via linq

I'm using json.net and i've a json data like that,
[
{
"ID":1098,
"Name":"JC",
"Issues":[
{
"PriorityLevel":"Low",
"State":"Open"
},
{
"PriorityLevel":"Low",
"State":"Open"
}
]
}
]
I just want to get childeren data from Issues via linq. I can reach parent but cannot children. If i reach children data directly i don't need to put more than one for loop.
Thank you.
You can just create a Json Object and extract the properties into an Anonymouse type that you can then query with Linq.
string response = #"[{
""ID"":1098,
""Name"":""JC"",
""Issues"":[
{
""PriorityLevel"":""Low"",
""State"":""Open""
},
{
""PriorityLevel"":""Low"",
""State"":""Open""
}
]}]";
var jsonObject = JObject.Parse(response);
var issues = jsonObject["Issues"].Select(x => new
{
PriorityLevel = (string)x.SelectToken("PriorityLevel"),
State = (string)x.SelectToken("State")
});
You use SelectToken to grab the children of Issues. Now you can query issues for whatever you want.
var lowPriorities = issues.Where(x => x.PriorityLevel == "Low");
Here is a direct link to the json.net page on "Deserializing Using LINQ Example".
Here you go
{
var json = #"[ {
""ID"":1098,
""Name"":""JC"",
""Issues"":[
{
""PriorityLevel"":""Low"",
""State"":""Open""
},
{
""PriorityLevel"":""Low"",
""State"":""Open""
}
]}]";
var a = JArray.Parse(json);
var issues = a.SelectMany (x => x["Issues"]);
var lowPriorities = issues.Where(x => ((string) x["PriorityLevel"]) == "Low");
}

Categories