How to Serialize Tree Structure Data From Database to JSON - c#

I have data in a data store which represents a tree structure. Following code represents how it can be described in a simple DataTable in C#.
using System.Data;
namespace DemoConsoleApp
{
class Program
{
static void Main(string[] args)
{
DataTable treeData = new DataTable();
treeData.Clear();
treeData.Columns.Add("NodeId", typeof(int));
treeData.Columns.Add("ParentNodeId", typeof(int));
treeData.Columns.Add("Data", typeof(string));
treeData.Columns.Add("AbsolutePath", typeof(string));
DataRow record1 = treeData.NewRow();
record1["NodeId"] = 1;
record1["ParentNodeId"] = 0;
record1["Data"] = "Dairy";
record1["AbsolutePath"] = "/Dairy/";
treeData.Rows.Add(record1);
DataRow record2 = treeData.NewRow();
record2["NodeId"] = 2;
record2["ParentNodeId"] = 1;
record2["Data"] = "Yoghurt";
record2["AbsolutePath"] = "/Dairy/Yoghurt";
treeData.Rows.Add(record2);
DataRow record3 = treeData.NewRow();
record3["NodeId"] = 3;
record3["ParentNodeId"] = 1;
record3["Data"] = "Cheese";
record3["AbsolutePath"] = "/Dairy/Cheese";
treeData.Rows.Add(record3);
DataRow record4 = treeData.NewRow();
record4["NodeId"] = 4;
record4["ParentNodeId"] = 2;
record4["Data"] = "Flavored";
record4["AbsolutePath"] = "/Dairy/Yoghurt/Flavored";
treeData.Rows.Add(record4);
}
}
}
I need to serialize the data into the following structure.
{
"TreeData": {
"NodeId" : "1",
"Data" : "Dairy",
"Children": [
{
"NodeId": "2",
"Data": "Yohurt",
"Children": [
{
"NodeId": "4",
"Data": "Flavored",
"Children": []
}
]
},
{
"NodeId": "3",
"Data": "Cheese",
"Children": []
}
]
}
}
How do i go about achieving this?

Create a new class:
class Node
{
public int NodeId;
public int ParentNodeId;
public string Data;
public string AbsolutePath;
public List<Node> Children = new List<Node>();
}
Then iterate over rows of the DataTable, create an appropriate tree structure and simply call JsonConvert.SerializeObject()
var nodes = new Dictionary<int, Node>();
foreach(DataRow record in treeData.Rows)
{
var node = new Node { NodeId = (int)record["NodeId"], ParentNodeId = (int)record["ParentNodeId"], Data = (string)record["Data"], AbsolutePath = (string)record["AbsolutePath"] };
nodes.Add(node.NodeId, node);
}
var rootNodeId = 1;
var rootNode = nodes[rootNodeId];
foreach(var keyValuePair in nodes)
{
var node = keyValuePair.Value;
if(node.NodeId != rootNodeId)
{
nodes[node.ParentNodeId].Children.Add(node);
}
}
string json = JsonConvert.SerializeObject(rootNode, Formatting.Indented);
Debug.WriteLine(json);
Output:
{
"NodeId": 1,
"ParentNodeId": 1,
"Data": "Dairy",
"AbsolutePath": "/Dairy/",
"Children": [
{
"NodeId": 2,
"ParentNodeId": 1,
"Data": "Yoghurt",
"AbsolutePath": "/Dairy/Yoghurt",
"Children": [
{
"NodeId": 4,
"ParentNodeId": 1,
"Data": "Flavored",
"AbsolutePath": "/Dairy/Flavored",
"Children": []
}
]
},
{
"NodeId": 3,
"ParentNodeId": 1,
"Data": "Cheese",
"AbsolutePath": "/Dairy/Cheese",
"Children": []
}
]
}

you need to make custom recursive function with Serialization Class
which will append Children till you get leaf node.
Below is example for that. please use your logic to append JsonString
var appendJsonString;
Private string PassParentId(int Id)
{
var childnodecount=0;
childnodecount=getchildnode(Id);
while(childnodecount>0)
{
getchilddata=getchildnode(Id);
childnodecount=getchilddata.count();
appendJsonString = getchilddata();
}
return appendJsonString;
}

My aproach would be to create an object (TreeDataNode, for example) and giving this object the following variables
public class TreeDataNode{
int nodeId;
string data;
List<TreeDataNode> children;
public void AddChildren(string relativePath, TreeDataNode node)
{
string[] path = relativePath.Split('/');
if(path.Count<2)
{
children.Add(node);
}else
{
string newPath = String.Join(#"/", relativePath.Split('\\').Skip(1));
children.First(n=> n.data==path[0]).AddChildren(newPath,node);
}
}
}
Once you populate this object, having a parent TreeDataNode and the subsequent children, you just have to serialize it with any standard JSON serializer, and you would have a structure just like the one you are trying to achieve.
Now, you just need to be sure to add your objects in order, never a parent after his children, and it will be alright. Create your parent TreeDataNode with the root object, and then call AddChildren for every object on this parent.

Related

Deserialize jagged array with unknown dimensions

I'm currently trying to parse a Json file in Unity using Json.NET and can parse 80% of the data at the moment. I've come across a section that is formatted like this.
{
"features": [
{
"coordinates": [
[
[ 1, 1 ],
[ 2, 2 ]
]
]
},
{
"coordinates": [
[ 1, 1 ],
[ 2, 2 ],
[ 3, 3 ]
]
},
{
"coordinates": [
[
[ 1, 2 ],
[ 1, 2 ]
]
]
},
{
"coordinates": [
[
[
[ 1, 2 ],
[ 1, 2 ]
]
]
]
}
]
}
The coordinates array may contain an unknown number of coordinates and it will be a jagged array with unknown dimensions as well. I'm unable to parse it as I'm not very well versed with Json deserialization.
Any help on how to approach this appreciated.
I'm not sure how much variety is in your data but here's a rough idea using recursion.
void ParseCoordinateJSON()
{
var coordinates = new List<int[]>();
var parsed = JObject.Parse(rawJson);
var featureArray = parsed.SelectToken("features");
var coordArray = featureArray.Children();
coordArray.ToList().ForEach(n => ExtractCoordinates(n.SelectToken("coordinates"), coordinates));
Debug.Log(string.Join("\n", coordinates.Select(c => $"({string.Join(", ", c)})")));
}
void ExtractCoordinates(JToken node, List<int[]> coordinates)
{
if (node == null)
{
return;
}
if (node.Children().Any(n => n.Type == JTokenType.Integer))
{
coordinates.Add(node.Children().Select(n => n.Value<int>()).ToArray());
return;
}
node.Children().Where(n => n.Type == JTokenType.Array).ToList().ForEach(n => ExtractCoordinates(n, coordinates));
}
Edit:
Here's it is without linq which might be easier to follow:
void ParseCoordinateJSONNoLinq()
{
var coordinates = new List<int[]>();
var parsed = JObject.Parse(rawJSON);
var featureArray = parsed.SelectToken("features");
// These will be the objects with a "coordinates" key and an array value.
var coordArray = featureArray.Children();
foreach(var node in coordArray)
{
ExtractCoordinatesNoLinq(node.SelectToken("coordinates"), coordinates);
}
Console.WriteLine(string.Join("\n", coordinates.Select(c => $"({string.Join(", ", c)})")));
}
void ExtractCoordinatesNoLinq(JToken node, List<int[]> coordinates)
{
var intValues = new List<int>();
// Step through each child of this node and do something based on its node type.
foreach(var child in node.Children())
{
// If the child is an array, call this method recursively.
if (child.Type == JTokenType.Array)
{
// Changes to the coordinates list in the recursive call will persist.
ExtractCoordinatesNoLinq(child, coordinates);
// The child type is an integer, add it to the int values.
} else if (child.Type == JTokenType.Integer)
{
intValues.Add(child.Value<int>());
}
}
// Since we found int values at this level, add them to the shared coordinates list.
if (intValues.Count > 0)
{
coordinates.Add(intValues.ToArray());
}
}
If the rest of your data is reliable, I would use typical data objects and de-serialize to them then add something like the above as a Custom JSON Converter for an object representing the jagged array.
public class MyDataObject {
public string SomeField {get; set;}
public Vector2 Position {get; set;}
[JsonProperty("features")]
public JaggedFeatures {get; set;}
}
public class JaggedFeatures {
public List<int[]> Coordinates {get; set;}
}
//...
JsonConvert.Deserialize<MyDataObject>(rawJSON, new JaggedFeaturesConverter())

There is no argument that corresponds to the required formal parameter

This is probably not the best way to be doing this but it's the best I know how to do with c#. I'm trying to create a dictionary and then convert it to json later. Right now I"m just trying to get the dictionary to match what I want in the json format later. Here is what I have so far:
`Dictionary<Dictionary<string, string>, Dictionary<string, List<List<Decimal>>>> testDict = new Dictionary<Dictionary<string, string>, Dictionary<string, List<List<Decimal>>>>() {
new Dictionary<string, string>() {
{ "test", "test" }
}
};`
This is giving me the following error:
There is no argument that corresponds to the required formal parameter
I don't know what could be causing this and any help would be great, thanks!
Here is the json structure I'm trying to replicate:
[
{
"target": "1",
"datapoints": [
[
67.0,
1491609600.0
]
]
},
{
"target": "2",
"datapoints": [
[
54.0,
1491091200.0
],
[
65.0,
1491177600.0
],
[
69.0,
1491609600.0
],
[
65.0,
1491696000.0
],
[
54.0,
1491868800.0
],
[
63.0,
1491955200.0
],
[
64.0,
1492214400.0
],
[
57.0,
1492732800.0
],
[
72.0,
1492819200.0
],
[
50.0,
1493337600.0
],
[
63.0,
1493424000.0
]
]
},
]
Ok, not the answer to your question, but it will help, is the correct structure for your JSON:
public class TargetClass
{
public string target{ get; set; }
public List<double[]> datapoints{ get; set; }
}
That's the base class. If you want to deserialize what you have in JSON you will do something like this (assuming you are using Newtonsoft Json, else change to the library you use):
var data = Newtonsoft.Json.JsonConvert.DeserializeObject<TargetClass[]>(theString);
And to serialize you would create something like this:
var items = new List<TargetClass>();
var target = new TargetClass{ target = "1", datapoints = new List<double[]>{ new double[]{ 67.0, 1491609600.0 } };
items.Add(target);
var ser = Newtonsoft.Json.JsonConvert.SerializeObject(items);
Using anonymous types you can create complex object hierarchies almost as easy as writing plain JSON:
var obj = new[] {
new {
target = "1",
datapoints = new [] {
new [] {
67.0,
1491609600.0
}
}
},
new {
target = "2",
datapoints = new [] {
new [] {
54.0,
1491091200.0
},
new [] {
65.0,
1491177600.0
},
}
}
};
var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
Demo: https://dotnetfiddle.net/jk8gho
If subcollections in this object are a subject to expansion, these collections should better be explicitly defined as List<dynamic>. Strictly speaking this dynamic is not necessary and explicit types may be used instead, but using dynamic simplifies the definition.
var obj = new List<dynamic> {
new {
target = "1",
datapoints = new List<dynamic> {
new [] {
67.0,
1491609600.0
}
}
},
new {
target = "2",
datapoints = new List<dynamic> {
new [] {
54.0,
1491091200.0
},
new [] {
65.0,
1491177600.0
},
}
}
};
var target2 = obj.Where(t => t.target == "2").Single();
target2.datapoints.Add(new [] {
64.0,
1492214400.0
});
target2.datapoints.Add(new[] {
57.0,
1492732800.0
});
var target3 = new {
target = "3",
datapoints = new List<dynamic> { }
};
target3.datapoints.Add(new[] {
72.0,
1492819200.0
});
obj.Add(target3);
var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
Demo: https://dotnetfiddle.net/d4ZUH8

Find n-level nested SubDocument MongoDB

I have this Document:
{
"$id": "1",
"DocType": "Unidade",
"Nome": "TONY",
"RG_InscricaoEstadual": "4347924938742",
"Setores": [
{
"$id": "9",
"Nome": "Child0",
"Setores": [
{
"$id": "10",
"Nome": "Child1",
"Setores": [
/* <n depth nested level> */
"$id": "11",
"Nome": "Child2",
"Id": "90228c56-eff2-46d2-a324-b04e3c69e15c",
"DocType": "Setor"
],
"Id": "60228c56-dff2-46d2-a324-b04e3c69e15b",
"DocType": "Setor"
}
],
"Id": "8457e1b7-39dc-462c-8f46-871882faea2c",
"DocType": "Setor"
}
]
}
How to query this SubDocument if I want to retrieve a Setor, for example
"Id": "60228c56-dff2-46d2-a324-b04e3c69e15b"
I know that if I know now many levels it is Nested, I can write a query to look for something like
Unidade.Setor.Id=="8457e1b7-39dc-462c-8f46-871882faea2c"
But How can I search for it for a unknown number of nested Levels, for example 1, 2, 3 n levels?
How to find the Setor with Id '90228c56-eff2-46d2-a324-b04e3c69e15c', for example?
Comments about how to solve this question will also be appreciated.
In c# we can create a recursive method to achieve this scenario.
Here is a BsonDocument I created:
BsonDocument doc = new BsonDocument {
{ "id","1"},
{ "DocType", "Unidade"},
{ "Nome", "TONY"},
{ "RG_InscricaoEstadual", "4347924938742"},
{ "Setores",new BsonArray {
new BsonDocument {
{ "id","9" },
{ "Nome", "Child0"},
{ "Setores", new BsonArray { new BsonDocument {
{ "id","10" },
{ "Nome", "Child1"},
{ "Setores", new BsonArray { new BsonDocument {
{ "id","11" },
{ "Nome", "Child2"},
{ "Id","90228c56-eff2-46d2-a324-b04e3c69e15c" },
{ "DocType", "Setor"}
}
}
},
{ "Id","60228c56-dff2-46d2-a324-b04e3c69e15b" },
{ "DocType", "Setor"}
}
}
},
{ "Id","8457e1b7-39dc-462c-8f46-871882faea2c" },
{ "DocType", "Setor"}
}
}
}
};
You can use Mongo c# Query method to get this BsonDocument from MongoDB.
Here is a recursive method I used to query document via "Id":
BsonDocument result = GetID(doc, "90228c56-eff2-46d2-a324-b04e3c69e15c");
public static BsonDocument GetID(BsonDocument doc, string queryId)
{
BsonDocument result = new BsonDocument();
if (doc.Elements.Where(c => c.Name == "Setores").Count() != 0)
{
foreach (var item in doc.GetElement("Setores").Value.AsBsonArray)
{
var id = item.AsBsonDocument.GetElement("Id").Value;
if (id == queryId)
{
result = item.AsBsonDocument;
break;
}
result = GetID(item.AsBsonDocument, queryId);
}
}
return result;
}
I hope this could give you some tips.
The only thing you could do is have nested queries i.e.
find({"Unidade.Setor.Id": ObjectId("8457e1b7-39dc-462c-8f46-871882faea2c")
find({"Unidade.Setor.Setor.Id": ObjectId("8457e1b7-39dc-462c-8f46-871882faea2c")
find({"Unidade.Setor.Setor.Setor.Id": ObjectId("8457e1b7-39dc-462c-8f46-871882faea2c")
Run then one after the other if the previous one fails.
But DON'T!!!
You should be storing these Setor records as separate documents. You can store them with references to each other and then query for them using lookup (like a join in SQL)
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/

c# NewtonJson Jarray check null/empty error

How we can check a json array is null or emty?
Json:
{
"productList": [
{
"id": 2440,
"serviceStatus": 1,
"listOfBillProductsExtras": [
{
"id": 2441,
"amount": 1,
"balance": 2,
}
],
"deskName": "Desk 1",
"onlyTime": "15:25"
},
{
"id": 2441,
"serviceStatus": 1,
"listOfBillProductsExtras": [ ],
"deskName": "Desk2",
"onlyTime": "15:27"
}
]
}
I try
JArray productList = JArray.Parse(content["productList"].ToString());
but it didn't work. (There was exp. Null Referance ) So, I want to check listOfBillProductsExtras array is null or empty. If not empty I will get the id, amount, balance.
Parse the Json object to jArray:
public ActionResult Method(object[] data)
{
var productList = Json.ParseJsonObjectToJArray(data, "productList");
if(jArray.Count > 0)
{
}
}
public class Json
{
public static JArray ParseJsonObjectToJArray(object[] data, string objectName)
{
dynamic jObject = JObject.Parse(data[0].ToString());
var info = jObject[objectName];
return info;
}
}
This should work
var found = JObject.Parse(json).SelectToken("productList[0].listOfBillProductsExtras[0].id");
where json is your input string.
found variable can be checked for null value.

Convert a Treeview to JSON using C#

I have to convert a TreeView object to JSON using C#. I am currently using JsonConvert.SerializeObject().
public class SubTreeNode : TreeNode
{
public CustomProperties customProperties;
}
public class CustomProperties
{
public string property1 = "Property1";
public string property2 = "Property2";
public string property3 = "Property3";
}
When tried it with JsonConvert.SerializeObject(treeView1.Nodes); it only returned the top nodes... not the child nodes, sub-child, and so on.
What is the easiest way to serialize and deserialize this TreeView object?
You will need to add methods that produce JSON from each node recursively.
Check out the related post: Serializing a Tree into Json Object.
I was confronted with the same problem: only top nodes are exported with the standard JSON.Net Serializer because, as fero under the above link rightly states, "your TreeNode class is serialized as an array because it implements IEnumerable". It further says you should decorate the TreeNode class with the JsonConverterAttribute. I didn't get this to work.
Here is an alternative. Please note that this may not be seen as the best way to do it. But it is pretty simple:
you are using standard TreeNode class and your data is stored in a TreeView
you want to use JSON.NET Serializer
Create a new hierarchical class with the information you want to export in JSON (in my case, I only want to export part of my treenode-properties, so creating a new simple class made sense twice):
'Simplified version of tree for json
'Compared to TreeNode class, this object is also serializable with the standard JSON.NET Serializer
Public Class JTree
Public children As New List(Of JTree)()
Private _name As String
Public Property name() As String
Get
Return _name
End Get
Set(value As String)
_name = value
End Set
End Property
Private _id As String
Public Property id() As String
Get
Return _id
End Get
Set(value As String)
_id = value
End Set
End Property
End Class
Recursively move your data from TreeView to a new JTree (our custom class):
Public Sub createSimplifiedJSONTree(parentNode As TreeNode, ByRef JTreeSimp As JTree)
'Start recursion on all subnodes
For Each childNode As TreeNode In parentNode.Nodes
Dim jchild As New JTree
jchild.id = childNode.Name
jchild.name = childNode.Text
JTreeSimp.children.Add(jchild)
createSimplifiedJSONTree(childNode, jchild)
Next
End Sub
Write simplified JSON tree to file using JSON.NET Serializer:
Private Sub WriteJSONfromTreeview()
Dim rootNode As TreeNode = TreeView1.Nodes(0)
Dim JTreeSimp As New JTree
createSimplifiedJSONTree(rootNode, JTreeSimp)
'serialize JSON directly to a file using JSON.Net Serializer
Using file__1 As StreamWriter = File.CreateText("c:\temp\test.txt")
Dim serializer As New JsonSerializer()
serializer.Formatting = Formatting.Indented
serializer.Serialize(file__1, JTreeSimp)
End Using
End Sub
Final txt (example):
{
"children": [
{
"children": [
{
"children": [
{
"children": [
{
"children": [
{
"children": [
{
"children": [
{
"children": [],
"name": "alcatraz",
"id": "021_3",
"size": 166
}
],
"name": "skyline",
"id": "031_3",
"size": 167
}
],
"name": "city",
"id": "041_5",
"size": 167
}
],
"name": "coit",
"id": "051_4",
"size": 169
}
],
"name": "tower",
"id": "061_3",
"size": 170
}
],
"name": "telegraphhill",
"id": "071_3",
"size": 170
}
],
"name": "coittower",
"id": "081_2",
"size": 170
},
{
"children": [
{
"children": [],
"name": "sunset",
"id": "071_112",
"size": 3
}
],
"name": "berkeley",
"id": "081_109",
"size": 3
},
{
"children": [
{
"children": [],
"name": "marin",
"id": "071_77",
"size": 3
}
],
"name": "marinheadlands",
"id": "081_110",
"size": 3
}
],
"name": "root",
"id": "000",
"size": 0
}
This is my solution:
First I create a node class that contains the data structure I want for the nodes, and a JSon method that serialize the object
[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
private class node
{
public node()
{
this.children = new List<node>();
}
public node(string _value, List<node> _children = null, bool _isChecked = false)
{
Value = _value;
isChecked = _isChecked;
if (_children != null)
{
children = _children;
}
}
[JsonProperty("value")]
public string Value { get; set; }
[JsonProperty("isChecked")]
public bool isChecked { get; set; }
[JsonProperty("children", NullValueHandling = NullValueHandling.Ignore)]
public List<node> children { get; set; }
[JsonIgnore]
public string JSon
{
get
{
return JsonConvert.SerializeObject(this);
}
}
}
I wrote a method that it's meant to be called recursively. This returns a list of the children nodes given a particular tree node
private List<node> RunNode(TreeNode node)
{
List<node> nodeOut = new List<node>();
foreach(TreeNode child in node.Nodes)
{
List<node> grandchild = RunNode(child);
nodeOut.Add(new node(child.Text, grandchild, child.Checked));
}
return nodeOut;
}
I wrote and update object method to create a root node where I can contain all the nodes of the treeview. I decided to use a root node instead of a list of nodes, because a list wouldn't have the JSon method that serialize the object.
private void ActualizarMenus()
{
List<node> parents= new List<node>();
foreach (TreeNode node in trw.Nodes)
{
List<node> childs = RunNode(node);
parents.Add(new node(node.Text,childs,node.Checked));
}
root = new node("root", parents, true);
}
The root object must be declared as a single node
private node root;
and you can just call the JSon method of root
MessageBox.show(root.JSon());
I hope this helps
using custom json serializer worked for me
var settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new OrgChartConverter() },
Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(tree, settings);
public class OrgChartConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Node<T>).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Node<T> node = (Node<T>)value;
JObject obj = new JObject();
obj.Add("Name", node.Value.Name);
obj.Add("Children", JArray.FromObject(node.Children, serializer));
obj.WriteTo(writer);
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}

Categories