getting value of children - c#

My simple json data as below
string _JsonData = #" {
"tm":{
"1":{
"pl":{
"11":{
"foo":"2"
},
"902":{
"foo":"70"
}
}
}
}";
I can get value of pl children's foo values (such as 2 and 70) as below code
JObject _JObject = JObject.Parse(_JsonData);
foreach (JToken _JTokenCurrent in _JObject["tm"]["1"]["pl"].Children())
{
MessageBox.Show(_JTokenCurrent["foo"].ToString());
}
So how can i get value of pl children's property values (such as 11 and 902)?
Thank you in advance.

OK I have solved as below;
JObject _JObject = JObject.Parse(_JsonData);
foreach (JToken _JTokenCurrent in _JObject["tm"]["1"]["pl"].Children())
{
// get values such as 11 and 902
JProperty _JTokenCurrentName = (JProperty)_JTokenCurrent;
MessageBox.Show(_JTokenCurrentName.Name);
/// get values such as 2 and 70
MessageBox.Show(_JTokenCurrent["foo"].ToString());
}

Not tested!
JObject _JObject = JObject.Parse(_JsonData);
foreach (JToken _JTokenCurrent in _JObject["tm"]["1"]["pl"].Children())
{
// Should be your 11 and 902
MessageBox.Show(_JTokenCurrent.Children().ToString());
// Should be your 2 nad 70
MessageBox.Show(_JTokenCurrent["foo"].ToString());
}

Related

Pick only one record from a JSON array in C#

I'm trying to extract a record from a JSON array but it doesn't seem to work.
Here my code :
DataTable table = ConvertJsonToDatatable(responseBody);
System.Windows.Forms.MessageBox.Show(table.Columns[1].ToString(), "transformation");
The MessageBox isn't showing. I have checked responseBody and the variable isn't empty at all.
Here the structure of this variable (and the JSON array rear)
{
"data":
[
[
1651217520000,
1.0562,
1.0562,
1.056,
1.0561,
0,
0
],
[
1651217580000,
1.0561,
1.0563,
1.0561,
1.0561,
0,
0
]
],
"events": null
}
public static DataTable ConvertJsonToDatatable(string jsonString)
{
var jsonLinq = JObject.Parse(jsonString);
// Find the first array using Linq
var linqArray = jsonLinq.Descendants().Where(x => x is JArray).First();
var jsonArray = new JArray();
foreach (JObject row in linqArray.Children<JObject>())
{
var createRow = new JObject();
foreach (JProperty column in row.Properties())
{
// Only include JValue types
if (column.Value is JValue)
{
createRow.Add(column.Name, column.Value);
}
}
jsonArray.Add(createRow);
}
return JsonConvert.DeserializeObject<DataTable>(jsonArray.ToString());
}
Does anyone have an idea of how to extract/pick one value from this array (which is a string in my code) ?
Have a nice week end everyone and thanks in advance
you have to fix table creating code
public static DataTable ConvertJsonToDatatable(string jsonString)
{
var jsonLinq = JObject.Parse(jsonString);
// Find the first array using Linq
var linqArray = jsonLinq.Descendants().Where(x => x is JArray).First();
//or maybe this would be enough
var linqArray = JObject.Parse(jsonString)["data"];
var jsonArray = new JArray();
foreach (var row in (JArray)linqArray)
{
var createdRow = new JObject();
var i = 0;
foreach (var item in row)
{
i++;
createdRow.Add("col" + i.ToString(), (string)item);
}
jsonArray.Add(createdRow);
}
return jsonArray.ToObject<DataTable>();
}
how to use
DataTable table = ConvertJsonToDatatable(responseBody);
string val = table.Rows[0].Field<string>("col2");
System.Windows.Forms.MessageBox.Show(val, "transformation");
i probably would be marked negative, but i try to explain how it looks like inside. i made example to show tow to get back list of arrays it might visualize for you.
void Main()
{
string json = "{\"data\":[[1651217520000,1.0562,1.0562,1.056,1.0561,0,0],[1651217580000,1.0561,1.0563,1.0561,1.0561,0,0]],\"events\":null}";
var obj = JObject.Parse(json);
foreach (JToken token in obj.FindTokens("data"))
{
foreach (JArray row in JArray.Parse(token.ToString()))
{
row.Dump();
row[0].Dump("first element");
}
}
}
public static class JsonExtensions
{
public static List<JToken> FindTokens(this JToken containerToken, string name)
{
List<JToken> matches = new List<JToken>();
FindTokens(containerToken, name, matches);
return matches;
}
private static void FindTokens(JToken containerToken, string name, List<JToken> matches)
{
if (containerToken.Type == JTokenType.Object)
{
foreach (JProperty child in containerToken.Children<JProperty>())
{
if (child.Name == name)
{
matches.Add(child.Value);
}
FindTokens(child.Value, name, matches);
}
}
else if (containerToken.Type == JTokenType.Array)
{
foreach (JToken child in containerToken.Children())
{
FindTokens(child, name, matches);
}
}
}
}
result would be an array of jarray
so you can build you DataTable rows
.Dump() is a left over from linqpad. Good tool. Same as console.write

Deep count for object properties

I'd like to count the number of properties within an object.
I have found several solutions but none really counts the child\nested propertie.
For instance, I have the following JSON object.
{
"id": "259a36d2-3852-425f-a70c-3f9477753210",
"name": "foo",
"type": "na",
"text": "ABC.pdf",
"left": 333,
"top": 130,
"w": 134,
"h": 34,
"customData": {
"A": "fa6css4ec8-8ffb-55bca4dde06a",
"name": "SDF.pdf",
"IsExists": false,
"PNumber": 1,
}
}
When trying the following I'm getting the result of 9, while I'm expecting 12 (which is the count of the entire properties).
JObject sourceJObject = JsonConvert.DeserializeObject<JObject>(json);
var res= sourceJObject.Count;
I'll be happy for assistant.
JObject.Count is documented as returning the count of child tokens - not all descendant tokens. So while you may want 12, you shouldn't really expect 12.
If you want the count of all descendants, you can use sourceJObject.Descendants().Count(). However, that will give you 13 rather than 12, because customData itself is a token.
If you want "all descendant properties where the value isn't an object" you can use OfType and Where, as shown in the code below. You should probably think about what you want to do with arrays though...
using Newtonsoft.Json.Linq;
string json = File.ReadAllText("test.json");
JObject jobject = JObject.Parse(json);
var properties = jobject.Descendants()
.OfType<JProperty>()
.Where(prop => prop.Value is not JObject);
Console.WriteLine($"Count: {properties.Count()}");
foreach (var property in properties)
{
Console.WriteLine($"{property.Name} = {property.Value}");
}
Output:
Count: 12
id = 259a36d2-3852-425f-a70c-3f9477753210
name = foo
type = na
text = ABC.pdf
left = 333
top = 130
w = 134
h = 34
A = fa6css4ec8-8ffb-55bca4dde06a
name = SDF.pdf
IsExists = False
PNumber = 1
try this
var obj=JObject.Parse(json);
var count=0;
CountProps(obj, ref count); // count=12
public void CountProps(JToken obj, ref int count)
{
if (obj is JObject)
foreach (var property in ((JObject)obj).Properties())
{
if (property.Value is JValue) count++;
if (property.Value is JObject) CountProps(property.Value, ref count);
if (property.Value is JArray) { count++; CountProps(property.Value, ref count); }
}
if (obj.GetType().Name == "JArray")
foreach (var property in ((JArray)obj))
if (property is JObject || property is JArray) CountProps(property, ref count);
}

Update all keys with a specific prefix in Newtonsoft JObject

How can I update all keys with a given prefix at all levels in a JObject with a specific value? e.g.
{
"nameOne": "dave",
"age": 23,
"foo": {
"nameTwo": "pete",
"age": 56
}
}
How can I update nameOne and nameTwo (name*) to "chris"?
if it goes no deeper than in your example you can try this
_settings = JObject.Parse(File.ReadAllText(SettingsFile));
_settings["nameOne"]="cris";
_settings["foo"]["nameTwo"]="cris";
or if you need some search, try this
var searchString = "name";
var newValue = "cris";
foreach (var property in _settings)
{
var key = property.Key;
if (property.GetType().Name != "JObject")
{
if (key.Contains(searchString)) _settings[key] = newValue;
}
else
{
JObject prop = _settings[key] as JObject;
foreach (var nestedProperty in prop)
{
var nestedKey = nestedProperty.Key;
if (nestedKey.Contains(searchString)) prop[nestedKey] = newValue;
}
}
}
instead of Contains you can use StartWith or EndsWith as well
it was tested in visual studio
{
"nameOne": "cris",
"age": 23,
"foo": {
"nameTwo": "cris",
"age": 56
}
}

Iterate IDictionary<string, string> with dynamic nested JSON as value in C#

My application receives Kafka messages that contain a Dictionary<string,string> as one of the properties, and its values could be a nested (however dynamic) json string, and I need to iterate through this unknown json. I am struggling to find a logic and even the best data structure to do this iteration.
Examples of the dictionary (mocked data):
//could have complex nested json string as value
"reward":"{
'xp':'200',
'gp':'150',
'loot':'{
'item':'sword',
'rarity': 'low'
}'
}",
"achievement":"win_match"
// while other messages might be simple
"type":"generic_xp",
"percent":"100",
"status":"complete"
Serialized version of a real message:
"{\"player_stats\":\"{\\\"assist\\\":0,\\\"deaths\\\":0,\\\"kills\\\":0,\\\"team_index\\\":2}\",\"round_attr\":\"{\\\"max_player_count\\\":4,\\\"rdur\\\":0,\\\"round\\\":1,\\\"team_player_count\\\":{\\\"team_1\\\":1,\\\"team_2\\\":0},\\\"team_score\\\":0}\",\"custom\":\"{\\\"armor\\\":\\\"armor_pickup_lv2\\\",\\\"balance\\\":550,\\\"helmet\\\":\\\"helmet_pickup_lv2\\\",\\\"misc\\\":[{\\\"count\\\":48,\\\"item_id\\\":\\\"shotgun\\\"},{\\\"count\\\":120,\\\"item_id\\\":\\\"bullet\\\"},{\\\"count\\\":2,\\\"item_id\\\":\\\"health_pickup_combo_small\\\"},{\\\"count\\\":2,\\\"item_id\\\":\\\"health_pickup_health_small\\\"}],\\\"weapon_1\\\":\\\"mp_weapon_semipistol\\\",\\\"weapon_2\\\":\\\"mp_weapon_shotgun_pistol\\\"}\",\"gdur\":\"0\"}"
To complicate even more
Create a model class is not an option because this json is completely dynamic
Flatting the dictionary is not possible because the json may have duplicated key names, but under different hierarchy
I cant request to change the Kafka message
What I am trying to do
The end user will define rules that I need to check if I find a match. For instance, a rule could be reward.xp == 200 or reward.loot.rarity == high or status == complete. These rules will be defined by the user so it cant be hardcoded, however I can decide with data structure to use to save them. So for each Kafka message, I have to iterate through that dictionary and try to find a match with the rules.
What I have tried
I ve tried JsonConvert.Deserialize to object, dynamic, ExpandoObject and none could handle the nested json hierarchy. They just got the 1st level correct. Same result with JObject.Parse as well.
Parse the JSON using whatever parser you like (I used Newtonsoft.Json).
Then recursively visit the hierarchy and copy each property to a flat list using the full path to each property value as a key. You can then iterate that flat list.
Edit: Comment requested supporting arrays, so this version does.
https://dotnetfiddle.net/6ykHT0
using System;
using Newtonsoft.Json.Linq;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
string json = #"{
'reward': {
'xp': '200',
'gp': '150',
'loot': {
'item': 'sword',
'rarity': 'low',
'blah': {
'socks': 5
}
},
'arrayofint': [1,2,3,4],
'arrayofobj': [
{
'foo': 'bar',
'stuff': ['omg!', 'what?!']
},
{
'foo': 'baz',
'stuff': ['a', 'b']
}
],
'arrayofarray': [
[1,2,3],
[4,5,6]
],
'arrayofheterogenousjunk': [
'a',
2,
{ 'objprop': 1 },
['staahp!']
]
},
'achievement': 'win_match'
}";
JObject data = JObject.Parse(json);
IList<string> nodes = flattenJSON(data);
Console.WriteLine(string.Join(Environment.NewLine, nodes));
}
private static IList<string> flattenJSON(JToken token)
{
return _flattenJSON(token, new List<string>());
}
private static IList<string> _flattenJSON(JToken token, List<string> path)
{
var output = new List<string>();
if (token.Type == JTokenType.Object)
{
// Output the object's child properties
output.AddRange(token.Children().SelectMany(x => _flattenJSON(x, path)));
}
else if (token.Type == JTokenType.Array)
{
// Output each array element
var arrayIndex = 0;
foreach (var child in token.Children())
{
// Append the array index to the end of the last path segment - e.g. someProperty[n]
var newPath = new List<string>(path);
newPath[newPath.Count - 1] += "[" + arrayIndex++ + "]";
output.AddRange(_flattenJSON(child, newPath));
}
}
else if (token.Type == JTokenType.Property)
{
var prop = token as JProperty;
// Insert the property name into the path
output.AddRange(_flattenJSON(prop.Value, new List<string>(path) { prop.Name }));
}
else
{
// Join the path segments delimited with periods, followed by the literal value
output.Add(string.Join(".", path) + " = " + token.ToString());
}
return output;
}
}
Output:
reward.xp = 200
reward.gp = 150
reward.loot.item = sword
reward.loot.rarity = low
reward.loot.blah.socks = 5
reward.arrayofint[0] = 1
reward.arrayofint[1] = 2
reward.arrayofint[2] = 3
reward.arrayofint[3] = 4
reward.arrayofobj[0].foo = bar
reward.arrayofobj[0].stuff[0] = omg!
reward.arrayofobj[0].stuff[1] = what?!
reward.arrayofobj[1].foo = baz
reward.arrayofobj[1].stuff[0] = a
reward.arrayofobj[1].stuff[1] = b
reward.arrayofarray[0][0] = 1
reward.arrayofarray[0][1] = 2
reward.arrayofarray[0][2] = 3
reward.arrayofarray[1][0] = 4
reward.arrayofarray[1][1] = 5
reward.arrayofarray[1][2] = 6
reward.arrayofheterogenousjunk[0] = a
reward.arrayofheterogenousjunk[1] = 2
reward.arrayofheterogenousjunk[2].objprop = 1
reward.arrayofheterogenousjunk[3][0] = staahp!
achievement = win_match
PREVIOUS VERSION (NO ARRAY SUPPORT)
This doesn't properly support arrays - it will output the contents of a property that is an array as the raw JSON - i.e. it won't traverse into the array.
https://dotnetfiddle.net/yZbwul
public static void Main()
{
string json = #"{
'reward': {
'xp': '200',
'gp': '150',
'loot': {
'item': 'sword',
'rarity': 'low',
'blah': {
'socks': 5
}
}
},
'achievement': 'win_match'
}";
JObject data = JObject.Parse(json);
IList<string> nodes = flattenJSON(data, new List<string>());
Console.WriteLine(string.Join(Environment.NewLine, nodes));
}
private static IList<string> flattenJSON(JObject obj, IList<string> path)
{
var output = new List<string>();
foreach (var prop in obj.Properties())
{
if (prop.Value.Type == JTokenType.Object)
{
output.AddRange(flattenJSON(prop.Value as JObject, new List<string>(path){prop.Name}));
}
else
{
var s = string.Join(".", new List<string>(path) { prop.Name }) + " = " + prop.Value.ToString();
output.Add(s);
}
}
return output;
}
Output:
reward.xp = 200
reward.gp = 150
reward.loot.item = sword
reward.loot.rarity = low
reward.loot.blah.socks = 5
achievement = win_match

Identifying that a property's value is an array

I have a JSON file:
{
"abn":"63119059513",
"acn":"119059513",
"business_structure":"Private Company",
"ngr_number":"1231231",
"cbh_number":"1231231",
"main_name":"Brickworks Building Products Pty Ltd",
"trading_name":"Brickworks",
"other_trading_names":"Austral Bricks",
"directors":[
{
"ID":"12114",
"ae_forms_filled_in_ID":"22739",
"name":"John Smith",
"dob":"1983-10-29",
"address_line_1":"123 Fake Street",
"address_line_2":"",
"address_line_city":"Fakeland",
"address_line_postcode":"2000",
"address_line_state":"New South Wales",
"address_line_country":"Australia",
"order_extract_id":null,
"director_found":null,
"drivers_lic":"",
"home_mortgage":"",
"phone":"",
"mobile":"",
"director_email":"",
"director_title":"Mr",
"director_position":"Director",
"dir_pdf_url":null
}
],
}
I want to determine if the value of any property has a structure of an array. The best I can come up with so far is:
StreamReader streamrr = new StreamReader("C:\\temp\\agfarm_example_udate.json", Encoding.UTF8);
string JSON = streamrr.ReadToEnd();
JObject CWFile = JObject.Parse(JSON);
foreach (JProperty property in CWFile.Properties())
{
// Do something
if (property.Value.ToString().Contains("["))
{
// Do something with the array
JArray items = (JArray)CWFile[property.Name];
foreach (JObject o in items.Children<JObject>())
{
foreach (JProperty p in o.Properties())
{
// Do something
}
}
}
}
To determine whether or not a property value has an array, I used the condition:
if (property.Value.ToString().Contains("["))
I'm just wondering if there is a better way of doing this check?
One way to do this is to check the JToken.Type property. Arrays are of type JTokenType.Array:
if (property.Value.Type == JTokenType.Array)
{
var items = (JArray)property.Value;
// Proceed as before.
}
Or, you can just try to cast to JArray:
if (property.Value is JArray)
{
var items = (JArray)property.Value;
// Proceed as before.
}
Both are preferable to checking property.Value.ToString().Contains("[") since a nested property might have an array value, thus causing a bracket to appear somewhere in the ToString() return.
If you want to recursively find every property with an array value, you can introduce an extension method:
public static class JsonExtensions
{
public static IEnumerable<JToken> WalkTokens(this JToken node)
{
if (node == null)
yield break;
yield return node;
foreach (var child in node.Children())
foreach (var childNode in child.WalkTokens())
yield return childNode;
}
}
And then do:
var CWFile = JToken.Parse(JSON)
var arrayProperties = CWFile.WalkTokens().OfType<JProperty>().Where(prop => prop.Value.Type == JTokenType.Array);
public void Traverse(string name, JToken j)
{
foreach (JToken token in j.AsJEnumerable())
if (token.Type == JTokenType.Object)
{
foreach (var pair in token as JObject)
{
string name_ = pair.Key;
JToken child = pair.Value;
Traverse(name, child);
}
}
else if (token.Type == JTokenType.Array) //an array property found
{
foreach (var child in token.Children())
Traverse(((JProperty)j).Name, child);
}
else if (token.Type == JTokenType.Property)
{
var property = token as JProperty; //current level property
Traverse(name, (JContainer)token);
}
else //current level property name & value
{
var nm = "";
var t = "";
if (j is JProperty)
{
nm = ((JProperty)j).Name;
t = Convert.ToString(((JProperty)j).Value);
}
t = Convert.ToString(token);
}
}
Call:
JSON = JObject.Parse(...);
Traverse("", JSON);
To parse and already parsed text for the same reason is not very wise :
if (property.Value.ToString().Contains("["))
Json structure is simple : objects, arrays, properties, values
http://www.json.org/
So array is a wellknown object for json and we are looking for it :
if (token.Type == JTokenType.Array) //an array property

Categories