Dynamically Building JSON Tree For Use In Javascript Components Using C#
Introduction
These days, many JavaScript components use JSON when it comes to working with data, the reason is obvious enough, but some components that build some kind of tree structure like this one, are going to need a JSON tree. Building a static JSON tree is easy, but how do you build a dynamic JSON tree and pass them to the components like the one mentioned? That's what this post is all about.
First Step: Building the Representation of a Tree as a Type
The first step we need to take, is to build a tree in C#, and then convert that tree into a JSON tree. But we probably have a tree structure somewhere in the database, the structure I saw most of the time to represent tree in a database is a record with an Id
and a ParentId
, so I'm going to use that as the representation of my tree in the database, but since we don't have any database, an In memory collection can serve our purpose, our collection is something like this:
public class Cate
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Name { get; set; }
}
var cats = new List<Cate>
{
new Cate {Id = 1, ParentId = null, Name = "First"},
new Cate {Id = 2, ParentId = null, Name = "Second"},
new Cate {Id = 3, ParentId = null, Name = "Thrid"},
new Cate {Id = 4, ParentId = null, Name = "Fourth"},
new Cate {Id = 5, ParentId = 4, Name = "SubFourth1"},
new Cate {Id = 6, ParentId = 4, Name = "SubFourth2"},
new Cate {Id = 7, ParentId = 5, Name = "Sub_SubFourth1"},
new Cate {Id = 8, ParentId = null, Name = "Sixth"},
new Cate {Id = 9, ParentId = null, Name = "Seventh"},
new Cate {Id = 10, ParentId = 9, Name = "SubSeventh1"},
new Cate {Id = 11, ParentId = 9, Name = "SubSeventh2"},
new Cate {Id = 12, ParentId = null, Name = "Eighth"},
new Cate {Id = 13, ParentId = null, Name = "Ninth"},
new Cate {Id = 14, ParentId = null, Name = "Tenth"},
new Cate {Id = 15, ParentId = null, Name = "Eleventh"},
new Cate {Id = 16, ParentId = 15, Name = "SubEleventh"},
new Cate {Id = 17, ParentId = 16, Name = "Sub_SubEleventh"},
};
Now we have the data we need for building a tree. What we need next is a type to represent our tree, every tree consists of many nodes, so we need to describe our node here, our type would be something like this:
class Node
{
[JsonProperty(PropertyName = "nodes")]
public List<Node> Children = new List<Node>();
public bool ShouldSerializeChildren()
{
return (Children.Count > 0);
}
public Node Parent { get; set; }
public int Id { get; set; }
public int? ParentId { get; set; }
[JsonProperty(PropertyName = "text")]
public string Name { get; set; }
}
To better understand our Node
class, let's take a look at this tree image:
Every Node
has a parent, so we declare a property of the type Node
called Parent
. In every Node
, we have some data, which in this case is Id
and ParentId
and Name
, and also, a Node
can have many nodes as children, so we declared a property called Children
that holds a list of Nodes as its children, and since we use Json.NET for our conversion form C# tree structure to JSON, ShouldSerializeChildren
is responsible for something called "Conditional Property Serialization", we need this because if we had a Node Without Children, The default behavior of Json.NET is that it includes this in our tree as empty collection of Nodes like this: "nodes": []
, and finally we have couple of Attributes that isn't doing anything here except changing the name of our property in serialization.
Second Step: Filling the Tree with Data
Now we have all the types necessary to build a simple tree, what we need to do now is to fill our tree structure with data, I've found a method for that here, and changed it slightly to fit my need:
public IEnumerable<Node> RawCollectionToTree(List<Cate> collection)
{
var treeDictionary = new Dictionary<int?, Node>();
collection.ForEach(x => treeDictionary.Add(x.Id,
new Node { Id = x.Id, ParentId = x.ParentId, Name = x.Name }));
foreach (var item in treeDictionary.Values)
{
if (item.ParentId != null)
{
Node proposedParent;
if (treeDictionary.TryGetValue(item.ParentId, out proposedParent))
{
item.Parent = proposedParent;
proposedParent.Children.Add(item);
}
}
}
return treeDictionary.Values.Where(x => x.Parent == null);
}
Here we've built a dictionary called treeDictionary
and we filled this dictionary with our data, key of this dictionary will be the Id of our Cate
type and the value will be the data of the node. After we filled our dictionary
, it's time to assign the node's Parent
and Children
property to its corresponding Parent
and Children
in the dictionary
, here we foreach
through our dictionary and if our node had a ParentId
, we go ahead and get that Parent
, and assign it to our current node's (item) Parent
property, and add to the Parent (proposedParent)
its Children
, which here is our Item, and return the dictionary
where Parent
is null
, now we have a tree in C#:
Now we need to convert this tree to JSON.
Third Step: Converting C# Tree To JSON Tree
Last step and the easiest, is to convert our C# tree to JSON, but there are a couple of issues you might face, which I'll explain next, for conversion we use this code:
var tree= roots.RawCollectionToTree(cats).ToList();
string json = JsonConvert.SerializeObject(tree, Formatting.Indented,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
});
Here, we first use our RawCollectionToTree
method to build a tree form our collection, then we convert it into JSON, there are two other things which might be worth pointing out, here we use a JsonSerializerSettings
with two properties, the first ReferenceLoopHandling
is responsible for ignoring the Self referencing looping, which can cause stackoverflow exception, and the second one which is kind of obvious is the null
value serialization, we tell the serializer to ignore the null
values. Now we have a JSON tree which resembles something like this:
[
{
"Id": 1,
"text": "First"
},
{
"Id": 2,
"text": "Second"
},
{
"Id": 3,
"text": "Thrid"
},
{
"nodes": [
{
"nodes": [
{
"Id": 7,
"ParentId": 5,
"text": "Sub_SubFourth1"
}
],
"Id": 5,
"ParentId": 4,
"text": "SubFourth1"
},
{
"Id": 6,
"ParentId": 4,
"text": "SubFourth2"
}
],
"Id": 4,
"text": "Fourth"
},
{
"Id": 8,
"text": "Sixth"
},
{
"nodes": [
{
"Id": 10,
"ParentId": 9,
"text": "SubSeventh1"
},
{
"Id": 11,
"ParentId": 9,
"text": "SubSeventh2"
}
],
"Id": 9,
"text": "Seventh"
},
{
"Id": 12,
"text": "Eighth"
},
{
"Id": 13,
"text": "Ninth"
},
{
"Id": 14,
"text": "Tenth"
},
{
"nodes": [
{
"nodes": [
{
"Id": 17,
"ParentId": 16,
"text": "Sub_SubEleventh"
}
],
"Id": 16,
"ParentId": 15,
"text": "SubEleventh"
}
],
"Id": 15,
"text": "Eleventh"
}
]
You can download the sample project from here. CodeProject