Related
I am needing to produce this JSON string with C#:
{
"in0": {
"children": [
{
"ValueObjectDescriptor": {
"fields": [
{
"FieldDescriptor": {
"name": "length",
"xpath": "#lenth"
}
},
{
"FieldDescriptor": {
"name": "height",
"xpath": "#height"
}
},
{
"FieldDescriptor": {
"name": "width",
"xpath": "#width"
}
}
],
"objectName": "Job",
"limit": 1,
"xpathFilter": "#openJob = 'true'"
}
}
]
}
}
Here is my code:
static string BuildJsonString()
{
var json = new
{
in0 = new
{
children = new
{
ValueObjectDescriptor = new
{
fields = new
{
FieldDescriptor = new
{
name = "length",
xpath = "#length",
},
FieldDescriptor = new
{
name = "height",
xpath = "#height",
},
FieldDescriptor3 = new
{
name = "width",
xpath = "#width",
},
objectName = "Job",
limit = "1",
xpathFilter = "#openJob='true'"
}
}
}
}
};
var jsonFormatted = JsonConvert.SerializeObject(json, Newtonsoft.Json.Formatting.Indented);
return jsonFormatted.ToString();
The issue I am having is that the compiler doesn't like me using "FieldDescriptor" multiple times, I get the error "An anonymous type cannot have multiple properties with the same name".
I am very new to JSON, so any advice would be greatly appreciated.
Note that not only is having multiple fields with the same name invalid C# code, having duplicate keys in the same object is also invalid JSON. You can be sure that there is no JSON that would need you to write duplicate field names to serialise it.
The "FieldDescriptor" JSON keys are actually part of different JSON objects, and these JSON objects are all in a JSON array under the key "fields":
[
{
"FieldDescriptor": {
"name": "length",
"xpath": "#lenth"
}
},
{
"FieldDescriptor": {
"name": "height",
"xpath": "#height"
}
},
{
"FieldDescriptor": {
"name": "width",
"xpath": "#width"
}
}
]
The [ ... ] denotes the array, and each pair of { ... } denotes a JSON object. So you should create an (implicitly typed) array of anonymous objects, each one with the FieldDescriptor property, rather than one object having all three of the proeperties:
fields = new[] // <--- create an array
{
new {
FieldDescriptor = new
{
name = "length",
xpath = "#length",
}},
new { // notice the new pairs of curly braces
FieldDescriptor = new
{
name = "height",
xpath = "#height",
}}, // here's the closing brace
new {
FieldDescriptor3 = new
{
name = "width",
xpath = "#width",
}},
objectName = "Job",
limit = "1",
xpathFilter = "#openJob='true'"
}
It looks like in the json "fields" is an array of objects where each object contains a "FieldDescriptor" field. In your C# code you are creating "fields" as an object with multiple fields not an array of objects.
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 getting json data from an API call, and need to read specific values from the data.
The following is a simplified version of the json:
{
"Modules":[
{
"ModuleId":370,
"Title":"Getting Oriented",
"Modules":[
{
"ModuleId":378,
"Title":"Artifacts",
"Modules":[
],
"Topics":[
{
"TopicId":379
},
{
"TopicId":380
},
{
"TopicId":381
}
]
}
],
"Topics":[
{
"TopicId":371
},
{
"TopicId":372
},
{
"TopicId":373
}
]
},
{
"ModuleId":389,
"Title":"Build Your Solution",
"Modules":[
{
"ModuleId":390,
"Title":"Core Platform Configuration",
"Modules":[
{
"ModuleId":392,
"Title":"Artifacts",
"Topics":[
{
"TopicId":393
}
]
}
],
"Topics":[
{
"TopicId":391
}
]
}
]
}
]
}
I convert it to a JObject and get all of the TopicIds using the following code.
var topicIds = jo.Descendants()
.OfType<JProperty>()
.Where(p => p.Name == "TopicId" &&
p.Value != null)
.ToList();
From there I need the TopicId's parent's ModuleId value.
I've tried the following but it returns all of the children of the parent.
foreach (var topicId in topicIds)
{
var moduleId = jo.DescendantsAndSelf()
.OfType<JProperty>()
.Where(p => p.Value == topicId.Value)
.Select(p => p.Parent)
.ToString();
}
I just want the "ModuleId" value from the "TopicId" parent
You already have the topicId property, so just navigate upward from there inside your loop to get the corresponding moduleId value:
var moduleId = (string)topicId.Parent.Parent.Parent.Parent["ModuleId"];
Fiddle: https://dotnetfiddle.net/gOkKHt
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.
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.