Build a Property List from a JSON Object c# - c#

I want to build the property list including property path of a json object.
I don't know the structure of the json or the keys that might be present. I'm after the keys at all levels (not the values of those keys).
{
"Primitive_1": "T1",
"Object_L1": {
"Object_L2": {
"Object_L3": {
"Object_L4": {
"Object_L5": {
"Object_L6": {
"Array_L7": [
{
"asdasdas": "SampleText1",
"WIDTH": "Width2"
},
{
"gh45gdfg": "SampleText2",
"WIDTH": "Width"
}
],
"12836hasvdkl": "SampleText3",
"WIDTH": "Width"
}
}
},
"712bedfabsmdo98": "SampleText4",
"WIDTH": "Width"
}
},
"ALIAS_ID": 1
},
"Primitive_2": "T2",
"Primitive_3": "T3",
"Primitive_4": "T4"
}
Desired output:
.Primitive_1 .Object_L1.Object_L2.Object_L3.Object_L4.Object_L5.Object_L6.Array_L7.0.asdasdas
.Object_L1.Object_L2.Object_L3.Object_L4.Object_L5.Object_L6.Array_L7.0.WIDTH
.Object_L1.Object_L2.Object_L3.Object_L4.Object_L5.Object_L6.Array_L7.1.gh45gdfg
.Object_L1.Object_L2.Object_L3.Object_L4.Object_L5.Object_L6.Array_L7.1.WIDTH
.Object_L1.Object_L2.Object_L3.Object_L4.Object_L5.Object_L6.12836hasvdkl
.Object_L1.Object_L2.Object_L3.Object_L4.Object_L5.Object_L6.WIDTH
.Object_L1.Object_L2.Object_L3.712bedfabsmdo98
.Object_L1.Object_L2.Object_L3.WIDTH
.Object_L1.ALIAS_ID
.Primitive_2
.Primitive_3
.Primitive_4
Having looked around I've gotten as far as the root nodes of the object. See fiddle (https://dotnetfiddle.net/wIl1Qw)
This seems to be relatively simple in JS (http://jsfiddle.net/alteraki/bt3zc1wt/) I've already reviewed several responses and I can't find a response in c# that solves this problem without knowing the keys in use (which I don't know)
Any help would be much appreciated.

Tree traversal algorithms are almost always recursive in nature.
As such, the following function does what you want:
private static IEnumerable<string> GetMembers(JToken jToken)
{
var members = new List<string>();
if (jToken is JObject)
{
var jObject = (JObject)jToken;
foreach (var prop in jObject.Properties())
{
if (prop.Value is JValue)
{
members.Add(prop.Name);
}
else
{
members.AddRange(GetMembers(prop.Value).Select(member => prop.Name + "." + member));
}
}
}
else if (jToken is JArray)
{
var jArray = (JArray)jToken;
for (var i = 0; i < jArray.Count; i++)
{
var token = jArray[i];
members.AddRange(GetMembers(token).Select(member => i + "." + member));
}
}
return members;
}
An example of the code running is available here.

Related

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

How to perform inner sort of an List<> in object?

I am trying to wrap my head around this, but can't think of any good way to do it. I have an object, for which the properties are one certain class (Though could contain multiple Classes, Lists etc.). These might be null or empty. An example of the structure can be seen below. So here I would have some string data, objects, List and even in the List a list object.
The goal is to get matched results from actual and Expected
{
"Name":"First Name",
"LastName":"Yeah",
"FavourityBooks":[
{
"BookName":"Hamlet",
"Printed":"Outside USA",
"PriceInDollars":100
},
{
"BookName": "Romeo And the Girl",
"Printed":"Somewhere In USA",
"PriceInDollars":99
}
],
"Debt":[
{
"c":[
{
"FriendName":"August",
"Amount":47
},
{
"FriendName":"April",
"Amount":53
}
],
"Sum":100
},
{
"ToBanks":[
{
"BankName":"Scotland BAnk",
"Amount":150
},
{
"BankName":"Some Other Bank",
"Amount":150
}
],
"Sum":300
}
]
}
One Way to go Sort Easily the Object by all properties
These results are coming from Graph Ql, but development has not implemented a sorting method as far as I was told.
How could I sort The inner list of FavourityBooks, by all three properties?
How could I sort The inner list of ToBanks or ToBanks and sort the list which they are in as well?
I don't know if I can achieve this by hardcoding each of the list sorting separately. some kind of recursive function should be used in order to sort each child object by all of its properties.
This though seems a very complex task to me. Any ideas?
Second Way to go is on the comparison level, more preferable
The way how I was thinking to do is maybe can sort these objects on the comparison, not prior, though I am lacking the ideas, how to sort them in this area as well.
E.g. A method:
public static List<EqualObjects> CompareObjects(object baseObject, object compareObject, object baseParentObject, object secondParentObject)
{
object basePropertyValue = null;
object secondPropertyValue = null;
var result = new List<EqualObjects>();
var baseObjectType = baseObject.GetType();
var secondObjectType = compareObject.GetType();
var baseObjectProperties = baseObjectType.GetProperties();
var secondObjectProperties = secondObjectType.GetProperties();
foreach (var baseProperty in baseObjectProperties)
{
var compareProperty = secondObjectProperties.FirstOrDefault(x => x.Name == baseProperty.Name);
//This gets the Object typeof(baseProperty) with the values
secondPropertyValue = compareProperty.GetValue(compareObject, null);
basePropertyValue = baseProperty.GetValue(baseObject, null);
var test = baseProperty.GetType().ToString();
if (basePropertyValue is null)
continue;
if (!baseProperty.PropertyType.FullName.Contains("System"))
{
result.AddRange(CompareObjects(basePropertyValue, secondPropertyValue, baseObject, compareObject));
continue;
}
if (baseProperty.PropertyType.FullName == "System.String")
{
result.AddRange(CompareSimpleElement(basePropertyValue, secondPropertyValue, baseObject, compareObject, baseProperty));
continue;
}
if (baseProperty.PropertyType.FullName == "System.Int32")
{
result.AddRange(CompareSimpleElement(basePropertyValue, secondPropertyValue, baseObject, compareObject, baseProperty));
continue;
}
if (baseProperty.PropertyType.FullName == "System.Decimal")
{
result.AddRange(CompareSimpleElement(basePropertyValue, secondPropertyValue, baseObject, compareObject, baseProperty));
continue;
}
//System.Collections.Generic.List
if (baseProperty.PropertyType.FullName.Contains("System.Collections.Generic.List") && baseProperty.PropertyType.FullName.Contains("System.String"))
{
var baseCollection = (List<string>)basePropertyValue;
var compareCollection = (List<string>)secondPropertyValue;
for (var i = 0; i < baseCollection.Count; i++)
{
if (baseCollection[i].Length > 0 && compareCollection[i].Length > 0)
{
result.AddRange(CompareSimpleList(baseCollection[i], compareCollection[i], baseObject,
compareObject, baseProperty,i));
}
}
}
if (baseProperty.PropertyType.FullName.Contains("System.Collections.Generic.List"))
{
var baseCollection = (List<object>)basePropertyValue;
var compareCollection = (List<object>)secondPropertyValue;
for (var i = 0; i < baseCollection.Count; i++)
{
}
}
}
return result;
}
Ideally, I could achieve this if I would be able to sort the BaseCollection and compareCollection by all properties (so if List, then sort by Object Properties) at first, then loop through and if another complex object would again sort it till it is a List which would always be the case once it gets here, other parts would be caught out previously.
if (baseProperty.PropertyType.FullName.Contains("System.Collections.Generic.List"))
{
var baseCollection = (List<object>)basePropertyValue;
var compareCollection = (List<object>)secondPropertyValue;
for (var i = 0; i < baseCollection.Count; i++)
{
}
}
Regards,

How to remove the attribute container from JSON in C#?

I have an issue with JSON that contains several elements, and I want to convert some JSON array of objects without the id that contain the element itself. Basically what I want is to convert this structure:
{
"SubscriptionStorages": {
"1": {
"Type": "subscriberstorage",
"SubscriberStorage_Id": 1,
"SubscriberStorage_AdminDescription": "JM Basic",
"SubscriberStorage_MaxStorage": 268435456000
},
"2": {
"Type": "subscriberstorage",
"SubscriberStorage_Id": 2,
"SubscriberStorage_AdminDescription": "JM Standard",
"SubscriberStorage_MaxStorage": 536870912000
}
}
}
to this structure:
{
"SubscriptionStorages": [
{
"Type": "subscriberstorage",
"SubscriberStorage_Id": 1,
"SubscriberStorage_AdminDescription": "JM Basic",
"SubscriberStorage_MaxStorage": 268435456000
},
{
"Type": "subscriberstorage",
"SubscriberStorage_Id": 2,
"SubscriberStorage_AdminDescription": "JM Standard",
"SubscriberStorage_MaxStorage": 536870912000
}
]
}
Is there any simple way to do it?
This is what I have so far, but it's not good...
What am I missing here?
List<string> items = new List<string>();
if (itemsList != null)
{
if (itemsList.Count > 0)
{
JToken outer = JToken.Parse(jsonBody);
foreach (JToken t in outer)
{
items.Add(t.ToString());
}
}
}
return items;
You can transform your JSON like this:
var jo = JObject.Parse(originalJson);
jo["SubscriptionStorages"] = new JArray(
jo["SubscriptionStorages"]
.Children<JProperty>()
.Select(jp => jp.Value)
);
var modifiedJson = jo.ToString();
Fiddle: https://dotnetfiddle.net/9sCx2M

How to access to all JProperties sub values in Json.net?

I`m trying to pars a Json in my C# code with Json.NET.
I want to print the child of each token after the parent.
my JSON string is something like this(but very longer):
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "FunctionExpression",
"id": null,
"params": [
{
"type": "Identifier",
"name": "E"
},
{
"type": "Identifier",
"name": "B"
}
]
}
}
}
and I`m trying this code for my purpose and in this first I check value of JProperties and if there was something starts with '[' I sub String it and parse it again other otherwise print that :(r is my Json string)
JObject a = JObject.Parse(r);
ArrayList ab= new ArrayList();
String reg=#"{[-a-zA-Z0-9_~,'"":\s]*}\s*,\s*{[-a-zA-Z0-9~,'"":\s]*}";
ab.Add(a);
for (int i = 0; i < ab.Count; i++ )
{
JObject d=ab[i] as JObject;
foreach (JProperty p in (d.Children()))
{
String val = p.Value.ToString();
if( val.StartsWith("[")){
val=val.Substring(2,val.Length-2);
if(Regex.Match(val,reg).Success)
{
String reg2 = #"},\s*{";
int num=Regex.Match(val,reg2).Index;
String val1 = val.Substring(0,num+1);
JObject newob = JObject.Parse(val1);
ab.Add(newob);
val = val.Substring(num+3);
newob = JObject.Parse(val);
ab.Add(newob);
}
if (!val.Equals("")) {
JObject newob=JObject.Parse(val);
ab.Add(newob);}
}
else if (val.StartsWith("{"))
{
if (!val.Equals(""))
{
JObject newob = JObject.Parse(val);
ab.Add(newob);
}
}
else
{
Console.WriteLine("value is: {0}", p.Value);
}
}
}
but there is always error for sub String....they always are incorrect!
could anyone help me? or offer me new way?
note:I don`t know the JSON string and it is every time different

Categories