JSON Object:
{
"Footer": "footer",
"RowType": 4,
"answers": [
{
"answer": 1,
"FooterInner": "innerfooter"
},
{
"answer": 2,
"FooterInner": "innerfooter2"
}
]
}
I need to remove all "integer" properties from JSON. JSON object may differ every time. So, consider that we do not know property key names.
Expected JSON Object:
{
"Footer": "",
"answers": [
{
"FooterInner": "innerfooter"
},
{
"FooterInner": "innerfooter2"
}
]
}
The above JSON object is just an example. JSON objects may differ every time (the user uploads JSON objects from UI) and I don't know the hierarchy and key/property names in JSON in advance. And JSON may contain N-nested properties.
I tried a lot things, but couldn't achieve the solution. Is there any way to do it?
You'll have to adjust (and CHECK) the RegEx for your needs but this method will strip out the integers from the json string.
private string CleanJson(string json)
{
var regEx = new Regex("(\"\\w*\": \\d*,)");
var jsonWithoutIntegers = regEx.Replace(json, string.Empty);
return jsonWithoutIntegers;
}
A cleaner way may be writing it up as an extension method.
public static class Extensions
{
public static JToken RemoveFieldTypes(this JToken token,params JTokenType []fieldTypes)
{
JContainer container = token as JContainer;
if (container == null) return token;
var tokensToRemove = new List<JToken>();
foreach (JToken el in container.Children())
{
JProperty p = el as JProperty;
if(p!=null && fieldTypes.Contains(p.Value.Type))
{
tokensToRemove.Add(el);
}
el.RemoveFieldTypes(fieldTypes);
}
foreach (JToken el in tokensToRemove)
{
el.Remove();
}
return token;
}
}
Now you can do the following.
JToken nodeList = JToken.Parse(strJson);
nodeList.RemoveFieldTypes(JTokenType.Integer);
Here is the solution:
static void Main(string[] args)
{
var json =
#"{
""Footer"": ""footer"",
""RowType"": 4,
""answers"":
[
{
""answer"": 1,
""FooterInner"": ""innerfooter""
},
{
""answer"": 2,
""FooterInner"": ""innerfooter2""
}
]
}";
JToken nodeList = JToken.Parse(json);
List<JTokenType> typesToRemove = new List<JTokenType>(){JTokenType.Boolean, JTokenType.Float, JTokenType.Integer};
removeFields(nodeList, typesToRemove);
Console.WriteLine(nodeList.ToString());
Console.ReadKey();
}
private static void removeFields(JToken token, List<JTokenType> typesToRemove)
{
JContainer container = token as JContainer;
if (container == null) return;
List<JToken> removeList = new List<JToken>();
foreach (JToken el in container.Children())
{
JProperty p = el as JProperty;
if (p != null && typesToRemove.Contains(p.Value.Type))
{
removeList.Add(el);
}
removeFields(el, typesToRemove);
}
foreach (JToken el in removeList)
{
el.Remove();
}
}
I am just sharing my first working code, and also for extension method solution you can use #Anu Visman's answer(I didn't try it).
Related
I wanted to parse the JSON with Newtonsoft.Json.Linq into to a tree format by going to each root level.
The actual problem i am facing is the content inside the allOf is not getting printed and its getting InvalidCast exception in JObject. I need help to print all the parent and child elements in the console application.
Here is the JSON:
{
"properties": {
"displayName": "Audit if Key Vault has no virtual network rules",
"policyType": "Custom",
"mode": "Indexed",
"description": "Audits Key Vault vaults if they do not have virtual network service endpoints set up. More information on virtual network service endpoints in Key Vault is available here: _https://learn.microsoft.com/en-us/azure/key-vault/key-vault-overview-vnet-service-endpoints",
"metadata": {
"category": "Key Vault",
"createdBy": "",
"createdOn": "",
"updatedBy": "",
"updatedOn": ""
},
"parameters": {},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.KeyVault/vaults"
},
{
"anyOf": [
{
"field": "Microsoft.KeyVault/vaults/networkAcls.virtualNetworkRules[*].id",
"exists": "false"
},
{
"field": "Microsoft.KeyVault/vaults/networkAcls.virtualNetworkRules[*].id",
"notLike": "*"
},
{
"field": "Microsoft.KeyVault/vaults/networkAcls.defaultAction",
"equals": "Allow"
}
]
}
]
},
"then": {
"effect": "audit"
}
}
},
"id": "/subscriptions/xxxxxx/providers/Microsoft.Authorization/policyDefinitions/wkpolicydef",
"type": "Microsoft.Authorization/policyDefinitions",
"name": "xyz"
}
And my code:
static JmesPath jmes = new JmesPath();
static void Main(string[] args)
{
string policyStr = "JSON GIVEN IN THE DESCRIPTION";
string str = jmes.Transform(policyStr, "properties.policyRule.if");
Convert(str);
}
public static void Convert(string json)
{
dynamic myObj = JsonConvert.DeserializeObject(json);
PrintObject(myObj, 0);
Console.ReadKey();
}
private static void PrintObject(JToken token, int depth)
{
if (token is JProperty)
{
var jProp = (JProperty)token;
var spacer = string.Join("", Enumerable.Range(0, depth).Select(_ => "\t"));
var val = jProp.Value is JValue ? ((JValue)jProp.Value).Value : "-";
Console.WriteLine($"{spacer}{jProp.Name} -> {val}");
foreach (var child in jProp.Children())
{
PrintObject(child, depth + 1);
}
}
else if (token is JObject)
{
foreach (var child in ((JObject)token).Children())
{
PrintObject(child, depth + 1);
}
}
}
I have installed JMESPath.Net NuGet package. Demo fiddle here.
Your basic problem is that, in PrintObject(JToken token, int depth), you do not consider the case of the incoming token being a JArray:
if (token is JProperty)
{
}
else if (token is JObject)
{
}
// Else JArray, JConstructor, ... ?
Since the value of "allOf" is an array, your code does nothing:
{
"allOf": [ /* Contents omitted */ ]
}
A minimal fix would be to check for JContainer instead of JObject, however, this does not handle the case of an array containing primitive values, and so cannot be considered a proper fix. (Demo fiddle #1 here.)
Instead, in your recursive code you need to handle all possible subclasses of JContainer including JObject, JArray, JProperty and (maybe) JConstructor. However, the inconsistency between JObject, which has two levels of hierarchy, and JArray which has only one, can make writing such recursive code annoying.
One possible solution to processing arrays and objects in a cleaner manner would be to hide the existence of JProperty entirely and represent that objects are containers whose children are indexed by name while arrays are containers whose children are indexed by integers. The following extension method does this job:
public interface IJTokenWorker
{
bool ProcessToken<TConvertible>(JContainer parent, TConvertible index, JToken current, int depth) where TConvertible : IConvertible;
}
public static partial class JsonExtensions
{
public static void WalkTokens(this JToken root, IJTokenWorker worker, bool includeSelf = false)
{
if (worker == null)
throw new ArgumentNullException();
DoWalkTokens<int>(null, -1, root, worker, 0, includeSelf);
}
static void DoWalkTokens<TConvertible>(JContainer parent, TConvertible index, JToken current, IJTokenWorker worker, int depth, bool includeSelf) where TConvertible : IConvertible
{
if (current == null)
return;
if (includeSelf)
{
if (!worker.ProcessToken(parent, index, current, depth))
return;
}
var currentAsContainer = current as JContainer;
if (currentAsContainer != null)
{
IList<JToken> currentAsList = currentAsContainer; // JContainer implements IList<JToken> explicitly
for (int i = 0; i < currentAsList.Count; i++)
{
var child = currentAsList[i];
if (child is JProperty)
{
DoWalkTokens(currentAsContainer, ((JProperty)child).Name, ((JProperty)child).Value, worker, depth+1, true);
}
else
{
DoWalkTokens(currentAsContainer, i, child, worker, depth+1, true);
}
}
}
}
}
Then your Convert() method now becomes:
class JTokenPrinter : IJTokenWorker
{
public bool ProcessToken<TConvertible>(JContainer parent, TConvertible index, JToken current, int depth) where TConvertible : IConvertible
{
var spacer = new String('\t', depth);
string name;
string val;
if (parent != null && index is int)
name = string.Format("[{0}]", index);
else if (parent != null && index != null)
name = index.ToString();
else
name = "";
if (current is JValue)
val = ((JValue)current).ToString();
else if (current is JConstructor)
val = "new " + ((JConstructor)current).Name;
else
val = "-";
Console.WriteLine(string.Format("{0}{1} -> {2}", spacer, name, val));
return true;
}
}
public static void Convert(string json)
{
var root = JsonConvert.DeserializeObject<JToken>(json);
root.WalkTokens(new JTokenPrinter());
}
Demo fiddle #2 here, which outputs:
allOf -> -
[0] -> -
field -> type
equals -> Microsoft.KeyVault/vaults
[1] -> -
anyOf -> -
[0] -> -
field -> Microsoft.KeyVault/vaults/networkAcls.virtualNetworkRules[*].id
exists -> false
[1] -> -
field -> Microsoft.KeyVault/vaults/networkAcls.virtualNetworkRules[*].id
notLike -> *
[2] -> -
field -> Microsoft.KeyVault/vaults/networkAcls.defaultAction
equals -> Allow
Related:
How to do recursive descent of json using json.net?
How to recursively populate a TreeView with JSON data
Iterate over a json input and create the treeview like hierarchy based on the "keys" taking account of the children "keys"
I am struggling with some logic for making tree structure out of string that I need to pull from database and add to TreeView control.
For the sake of explanation, i have created a small example in winforms
Let's say I have some list of strings
private List<string> strings;
public Form1()
{
InitializeComponent();
strings = new List<string>
{
"Root1.Parrent1",
"Root1.Parrent2",
"Root1.Parrent3.Child1",
"Root1.Parrent4.Child2",
"Root2.newParrent1.newChild1",
"Root2.newParrent1.newChild2",
"Root2.NewParrent2"
};
}
So I want to distinct every repeated element and make a hierarchy that looks like this, note that I have repeated roots as well as parrents (newparrent1)
main root {
root1( root2(
parrent1 newParrent1(
parrent2 --newChild1
parrent3(--child1) --newChild2)
parrent4(--child2) newParrent2)
}
So far my logic is cartoon, I don't even know where to begin, I don't know how menu sub nodes will one node have, done some research, but couldn't get the answer.
I have created example only for the first item of strings list, just to see how my logic would go.
string[] splitsArray = strings[0].Split('.');
TreeNode node = new TreeNode();
node.Text = splitsArray[0];
//creating sub node
TreeNode subNode = new TreeNode();
subNode.Text = splitsArray[1];
//adding
node.Nodes.Add(subNode);
treeView1.Nodes[0].Nodes.Add(node);
Can't even see how could I put this logic into a loop.
Any suggestion is helpful, even pseudo code. thank you for your time.
Since tagged it as an algorithm question I will provide the algorithm only and leave the coding part as your exercise.
First you make a Dictionary of all the nodes with the key being the node name and value as the node object
Dictionary<string, Node> nodes;
and a parent table
Dictionary<string, string> childParent;
then you iterate thru all the strings and split the parts, for each part you check if the name exists in the nodes Dictionary, if not then add them in
and add the parent to the parent table. For example
Root2.newParrent1.newChild1
You will add (newParrent1, Root2) and (newChild1, newParrent1) to the childParent table
Now you have a dictionary of the nodes and parent, we can start making the dependency.
you go thru the childParent table we just created, and add the node to their corresponding parent's Children property
for example if you have (newParrent1, Root2)
you will be doing nodes["Root2"].Children.Add(nodes["newParrent1"]);
Oh, did we forget about the root node?
I will leave that as exercise too.
public class Program
{
public static void Main(string[] args)
{
//Your code goes here
Console.WriteLine("Hello, world!");
List<string> strings;
strings = new List<string>()
{
"Root1.Parrent1",
"Root1.Parrent2",
"Root1.Parrent3.Child1",
"Root1.Parrent4.Child2",
"Root2.newParrent1.newChild1",
"Root2.newParrent1.newChild2",
"Root2.NewParrent2"
};
eat e = new eat(strings);
Console.WriteLine(e.root.ToJson());
Console.WriteLine("Press enter to close...");
Console.ReadLine();
}
}
public class eat
{
public node root;
public eat(List<string> l)
{
root = new node("root");
foreach(string s in l)
{
addRow(s);
}
}
public void addRow(string s)
{
List<string> l = s.Split('.').ToList<String>();
node state = root;
foreach(string ss in l)
{
addSoon(state, ss);
state = getSoon(state, ss);
}
}
private void addSoon(node n, string s)
{
bool f = false;
foreach(node ns in n.soon)
{
if (ns.name == s) { f = !f; }
}
if (!f) { n.soon.Add(new node(s)); }
}
private node getSoon(node n,string s)
{
foreach (node ns in n.soon)
{
if (ns.name == s) { return ns; }
}
return null;
}
}
public class node
{
public node(string n)
{
name = n;
soon = new List<node>();
}
public string name;
public List<node> soon;
public string ToJson()
{
String s = "";
s = s + "{\"name\":\"" + name + "\",\"soon\":[";
bool f = true;
foreach(node n in soon)
{
if (f) { f = !f; } else { s = s + ","; }
s = s + n.ToJson();
}
s = s + "]}";
return s;
}
}
this is a solution without the use of dictionaries
we have an eater that has the task of processing and stacking node-like objects
then we have the node that deals with managing the element and the children.
he class tojson is used to verify correct operation
outpot:
{
"name": "root",
"soon": [
{
"name": "Root1",
"soon": [
{
"name": "Parrent1",
"soon": [
]
},
{
"name": "Parrent2",
"soon": [
]
},
{
"name": "Parrent3",
"soon": [
{
"name": "Child1",
"soon": [
]
}
]
},
{
"name": "Parrent4",
"soon": [
{
"name": "Child2",
"soon": [
]
}
]
}
]
},
{
"name": "Root2",
"soon": [
{
"name": "newParrent1",
"soon": [
{
"name": "newChild1",
"soon": [
]
},
{
"name": "newChild2",
"soon": [
]
}
]
},
{
"name": "NewParrent2",
"soon": [
]
}
]
}
]
}
UPDATE
I've just added recursive method into node class that will return TreeNode obj so I can put in TreeView, thank you , this is very useful
public TreeNode GetRoot(List<node> nodes, TreeNode parrent)
{
foreach (var node in nodes)
parrent.Nodes.Add(GetRoot(node.children, new TreeNode(node.name)));
return parrent;
}
!!!!--- Steve's solution is more correct, using dictionaries ---!!!
If you are looking for a way to add those strings to TreeView you can use following code. The answer is trying to do it with minimal code:
treeView1.BeginUpdate();
foreach (var s in strings)
{
TreeNode node = null;
foreach (var text in s.Split('.'))
{
var key = (node == null ? $"{text}" : $"{node.Name}.{text}");
var nodes = (TreeNodeCollection)((dynamic)node ?? (dynamic)treeView1).Nodes;
node = (nodes.Find(key, false)).FirstOrDefault() ?? nodes.Add(key, text);
}
}
treeView1.EndUpdate();
The reason for using dynamic in above code, is because both TreeView and TreeNode have Nodes collection. You can use dynamic or you can have two different branches of code (if(node == null){...}/else{...}) which are quiet similar.
Just for your information TreeNodes collection has a Find method which allows you to find a node by key. The answer relies on setting key for the node when adding which allows us to find it later simply.
I'm getting from client json string:
{ "Client": { "Name": "John" } }
but for the further handling I need the following json:
{ "client": { "name": "John" } }
I tried something like that, but it didn't help:
public class LowerCaseNamingStrategy : NamingStrategy
{
protected override string ResolvePropertyName(string name)
{
return name.ToLower();
}
}
and
var settings = new JsonSerializerSettings();
settings.ContractResolver = new DefaultContractResolver { NamingStrategy = new LowerCaseNamingStrategy() };
var json = JsonConvert.DeserializeObject(input.DataJson, settings);
JSON is dynamic object, so I don't know properties are there.
How can I do that with c#? With using Newtonsoft.Json or may be with using Xml.
If I understood you correctly, you need to modify properties in your Json string, but not convert the Json into object.
In this case you can try to parse Json into JObject and replace properties in that object.
private static void ChangePropertiesToLowerCase(JObject jsonObject)
{
foreach (var property in jsonObject.Properties().ToList())
{
if(property.Value.Type == JTokenType.Object)// replace property names in child object
ChangePropertiesToLowerCase((JObject)property.Value);
property.Replace(new JProperty(property.Name.ToLower(),property.Value));// properties are read-only, so we have to replace them
}
}
sample:
var jsonString = #"{ ""Client"": { ""Name"": ""John"" } }";
var jobj = JObject.Parse(jsonString, new JsonLoadSettings());
ChangePropertiesToLowerCase(jobj);
var stringWithLowerCaseProperties = jobj.ToString(Formatting.None);
Try LowercaseContractResolver instead
var settings = new JsonSerializerSettings();
settings.ContractResolver = new LowercaseContractResolver();
var json = JsonConvert.DeserializeObject(input.DataJson, settings);
Extending Anton Semenov answer for cases when JSON can contain an Array of Objects:
private static void ChangePropertiesToLowerCase(JObject jsonObject)
{
foreach (var property in jsonObject.Properties().ToList())
{
if (property.Value.Type == JTokenType.Object) // replace property names in child object
ChangePropertiesToLowerCase((JObject)property.Value);
if (property.Value.Type == JTokenType.Array)
{
var arr = JArray.Parse(property.Value.ToString());
foreach (var pr in arr)
{
ChangePropertiesToLowerCase((JObject)pr);
}
property.Value = arr;
}
property.Replace(new JProperty(property.Name.ToLower(CultureInfo.InvariantCulture), property.Value)); // properties are read-only, so we have to replace them
}
}
All other solutions here modify the original object. Here's an immutable version which returns a new object with lowercase properties:
public static class JsonExtensions
{
public static JToken ToLowerRecursive(this JToken token)
{
if (token is JObject jobj)
return new JObject(jobj.Properties().Select(x => new JProperty(x.Name.ToLowerInvariant(), x.Value.ToLowerRecursive())));
if (token is JArray jarr)
return new JArray(jarr.Select(x => x.ToLowerRecursive()));
return token;
}
}
This is easy way without Regex. Replace every [{ "A] with [{ "a]
var json = "{ \"Client\": { \"Name\": \"John\" } }";
var newJson = string.Empty;
foreach (var w in json.Split(new[] { "{ \"" }, StringSplitOptions.RemoveEmptyEntries))
{
if (w[0] != null)
{
newJson += "{ \"" + (w[0].ToString().ToLower()) + w.Remove(0,1);
}
}
result:
"{ \"client\": { \"name\": \"John\" } }"
I want to take some Json and parse it in to a collection of key/value pairs, but some of the values will be dictionaries themselves. I tried the usual Newtonsoft deserialization. It's close, but not quite right. The end result must be a dictionary, not a strongly typed class.
This is some example Json:
{
"JobNumber": 1010,
"Asset": null,
"JobNotes": [
{
"NoteText": "It's not working.",
"NoteType": "Complaint"
},
{
"NoteText": "Needs to be fixed",
"NoteType": "Job"
}
]
}
This is the code I used to deserialize:
var json = File.ReadAllText(#"c:\temp\job.json");
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
The result is almost correct, but the value of the item with a key of "JobNotes" is just json string. I want the parser to recurse in and deserialise the inner Json to a further dictionary of strings and objects. Is there a way I can do this with the Newtonsoft library? Or, is there another library that will do the trick? Can I hook in to the parsing method to override the functionality at that point in time?
This is a modified version of #DanielKeogh's code. It works well.
class Program
{
static void Main(string[] args)
{
var json = File.ReadAllText(#"c:\temp\job3.json");
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
RecurseDeserialize(result);
}
private static void RecurseDeserialize(Dictionary<string, object> result)
{
//Iterate throgh key/value pairs
foreach (var keyValuePair in result.ToArray())
{
//Check to see if Newtonsoft thinks this is a JArray
var jarray = keyValuePair.Value as JArray;
if (jarray != null)
{
//We have a JArray
//Convert JArray back to json and deserialize to a list of dictionaries
var dictionaries = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(jarray.ToString());
//Set the result as the dictionary
result[keyValuePair.Key] = dictionaries;
//Iterate throught the dictionaries
foreach (var dictionary in dictionaries)
{
//Recurse
RecurseDeserialize(dictionary);
}
}
}
}
}
This modified Json shows how deep it goes:
{
"JobNumber": 1010,
"Asset": null,
"JobNotes": [
{
"NoteText": "It's not working.",
"NoteType": "Complaint"
},
{
"NoteText": "Needs to be fixed",
"NoteType": "Job",
"JobNoteNotes": [
{
"Something": 1,
"Something2": "Test"
}
]
}
]
}
The result ends three dictionaries deep so that I can get at the "Something" value by key.
This can be done with a little recursion. I'll leave defining IsJson up to you as an academic exercise. :)
Dictionary<string, object> RecursiveDeserialize(string json)
{
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
foreach (var pair in result.ToArray())
{
if(IsJson(pair.Value))
{
result[pair.Key] = RecursiveDeserialize(pair.Value);
}
}
return result;
}
Using this object for json string
public class JobNote
{
public string NoteText { get; set; }
public string NoteType { get; set; }
}
public class ListJob
{
public int JobNumber { get; set; }
public object Asset { get; set; }
public List<JobNote> JobNotes { get; set; }
}
Then you can deserialize it
Part of my code serializes file paths of a machine into JSON in the below format. I am struggling to take this JSON and put the file paths back together again. I am using Newtonsoft JSON lib; I find it's excellent for building JSON. As you can see, my JSON has nested objects.
The JSON I have:
{
".": {
"proc": {
"15": {
"task": {
"15": {
"exe": {},
"mounts": {
"list_of_files": [
"mounts.xml"
]
},
"mountinfo": {
"list_of_files": [
"mountinfo.xml"
]
},
"clear_refs": {
"list_of_files": [
"clear_ref.xml"
]
}
}
}
},
"14": {
"loginuid": {
"list_of_files": [
"loginuid.xml"
]
},
"sessionid": {
"list_of_files": [
"sessionid.xml"
]
},
"coredump_filter": {
"list_of_files": [
"coredump_filter.xml"
]
},
"io": {
"list_of_files": [
"io.xml"
]
}
}
}
}
}
The array I want to generate from this.
string[] dirArray = {
"./proc/15/task/15/exe",
"./proc/15/task/15/mounts/mounts.xml",
"./proc/15/task/15/mountinfo/mountinfo.xml",
"./proc/15/task/15/clear_refs/clear_ref.xml",
"./proc/14/loginuid/loginuid.xml",
"./proc/14/sessionid/sessionid.xml",
"./proc/14/coredump_filter/coredump_filter.xml",
"./proc/14/io/io.xml"
}
My efforts so far-- I deserialised the JSON into a dynamic variable but I'm not sure how to handle two issues:
My JSON format is unknown, I don't know how deep the objects go, how can I handle this?
How do I work with dynamic variables when they are defined at run-time?
EDIT
Sorry, my original JSON format was wrong, so it doesn't work with the answer provided by user12864. I'm getting an error: Unable to cast object of type 'Newtonsoft.Json.Linq.JArray' to type 'Newtonsoft.Json.Linq.JObject'.
Here is a fiddle showing where I'm at so far.
#user12864 has the right idea in his answer, but the code needs to be adjusted to account for the fact that each directory can have an array of files rather a single "file" object (you really should have mentioned that in your question originally). Here is an updated method to handle that:
private static void AddToFileList(JObject jo, List<string> list, string prefix)
{
foreach (var kvp in jo)
{
if (kvp.Key == "list_of_files")
{
foreach (string name in (JArray)kvp.Value)
{
list.Add(prefix + name);
}
}
else
{
JObject dir = (JObject)kvp.Value;
if (dir.Count == 0)
{
list.Add(prefix + kvp.Key);
}
else
{
AddToFileList(dir, list, prefix + kvp.Key + "/");
}
}
}
}
Full demo:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
class Program
{
static void Main(string[] args)
{
string json = #"
{
""."": {
""proc"": {
""15"": {
""task"": {
""15"": {
""exe"": {},
""mounts"": {
""list_of_files"": [
""mounts.xml""
]
},
""mountinfo"": {
""list_of_files"": [
""mountinfo.xml""
]
},
""clear_refs"": {
""list_of_files"": [
""clear_ref.xml""
]
}
}
}
},
""14"": {
""loginuid"": {
""list_of_files"": [
""loginuid.xml""
]
},
""sessionid"": {
""list_of_files"": [
""sessionid.xml""
]
},
""coredump_filter"": {
""list_of_files"": [
""coredump_filter.xml""
]
},
""io"": {
""list_of_files"": [
""io.xml""
]
}
}
}
}
}";
JObject jo = JObject.Parse(json);
foreach (string path in CreateFileList(jo))
{
Console.WriteLine(path);
}
}
private static List<string> CreateFileList(JObject jo)
{
List<string> ret = new List<string>();
AddToFileList(jo, ret, "");
return ret;
}
private static void AddToFileList(JObject jo, List<string> list, string prefix)
{
foreach (var kvp in jo)
{
if (kvp.Key == "list_of_files")
{
foreach (string name in (JArray)kvp.Value)
{
list.Add(prefix + name);
}
}
else
{
JObject dir = (JObject)kvp.Value;
if (dir.Count == 0)
{
list.Add(prefix + kvp.Key);
}
else
{
AddToFileList(dir, list, prefix + kvp.Key + "/");
}
}
}
}
}
Output:
./proc/15/task/15/exe
./proc/15/task/15/mounts/mounts.xml
./proc/15/task/15/mountinfo/mountinfo.xml
./proc/15/task/15/clear_refs/clear_ref.xml
./proc/14/loginuid/loginuid.xml
./proc/14/sessionid/sessionid.xml
./proc/14/coredump_filter/coredump_filter.xml
./proc/14/io/io.xml
Fiddle: https://dotnetfiddle.net/r8CkI2
This should give exactly what you're looking for; just create a JObject with JObject.Parse and pass it to CreateFileList. It won't handle malformed JSON in any nice way.
static List<string> CreateFileList(JObject j)
{
List<string> ret = new List<string>();
AddToFileList(j, ret, "");
return ret;
}
static void AddToFileList(JObject j, List<string> dest, string prefix)
{
if (prefix.Length != 0)
prefix = prefix + '/';
foreach (var kvp in j)
{
var jnext = (JObject)kvp.Value;
if (kvp.Key == "file")
dest.Add(prefix + (string)jnext["name"]);
else
AddToFileList(jnext, dest, prefix + kvp.Key);
}
}
Fiddle at https://dotnetfiddle.net/dQQ4tI
Update:
Here is a revised answer, after you clarified your requirement of:
The JavaScript Object Notation is built on the server, edited by user through a hierarchical tree interface component. That can be crawled incredibly easy.
So in essence your utilizing a component, in which your hoping to build simple JavaScript Object Notation derived from the component. Your User Interface will be unknown, so I'll make some presumptions.
Build our Object:
public class XmlPath
{
public string Location { get; set; }
}
The XmlPath will represent our object. Which will be basic auto property.
Add Content to our Object:
private List<XmlPath> AddXmlPath(List<string> location)
{
List<XmlPath> content = new List<XmlPath>();
foreach(string item in location)
content.Add(new XmlPath() { Location = item });
return content;
}
This will be incredibly simple method, it will take a large List<string> of your user data and add them to your XmlPath object.
Remove content from our Object:
private List<XmlPath> RemoveXmlPath(List<XmlPath> root, string location)
{
root.Remove(new XmlPath() { Location = location });
return root;
}
Those two methods truly don't need to be, I'm just demonstrating and showing how you could. Plus it will outline the intentions a bit easier for you to implement. Please note this is incredibly crude approach.
Serialize / Deserialize Our Object to JavaScript Objection Notation:
JavaScriptSerializer serializer = new JavaScriptSerializer();
var xmlPath = AddXmlPath(List<string> location);
var result = serializer.Serialize(xmlPath);
var deserialize = serializer.Deserialize(List<XmlPath>>(result);
Our content is exposed now through a basic loop:
foreach(XmlPath item in deserialize)
{
// Exposed Model via 'item.Location'
}
You'll simply need to correlate this core functionality to your implementation. The approach is crude, quite rudimentary, definitely will need to be improved on for production. However this should get you started with:
Serialize data on the server.
Deserialize server data.
Manipulating the object.
Hope this is better for you.