Serializing a Tree into Json Object - c#

I have the following classes:
TreeNode.cs
public class TreeNode : IEnumerable<TreeNode>
{
public readonly Dictionary<string, TreeNode> _children = new Dictionary<string, TreeNode>();
public readonly string Id;
public TreeNode Parent { get; private set; }
public TreeNode(string id)
{
this.Id = id;
}
public TreeNode GetChild(string id)
{
return this._childs[id];
}
public void Add(TreeNode item)
{
if (item.Parent != null)
{
item.Parent._childs.Remove(item.Id);
}
item.Parent = this;
this._childs.Add(item.Id, item);
}
public IEnumerator<TreeNode> GetEnumerator()
{
return this._childs.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public int Count
{
get { return this._childs.Count; }
}
}
FolderStructureNode.cs
public class FolderStructureNode : TreeNode
{
//Some properties such as FolderName, RelativePath etc.
}
So, when I have an object of type FolderStructureNode it is essentially a tree datastructure where each node represents a folder.
I want to serialize this object into a JsonObject. I have tried both - JavaScriptSerializer and NewtonSoft. In both cases I get an output as -
[
[
[]
],
[
[]
],
[]
]
At the time of serialization, the tree looks something like this:
How do I serialize it in order to get the correct json object? Do I have to traverse the tree and create the json myself?

As I said in my comment, your TreeNode class is serialized as an array because it implements IEnumerable<TreeNode>. So the only thing you will ever see when a TreeNode is serialized, are the children of a node. These children are (of course) also serialized as an array - down to the last leaf node. The leaf nodes don't have children and are therefore serialized as empty arrays. So that's why your JSON output looks like this.
You didn't exactly specify what JSON output you want to have, but I think what you want is like this:
{
"Sales Order": { },
"RFP":
{
"2169": { }
},
"Purchase Order":
{
"2216": { }
}
}
To achieve this, your TreeNode class must be serialized as object. In my opinion (and assuming/suggesting you're using Newtonsoft Json.NET), you should write a custom converter class which might look like this:
class TreeNodeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// we can serialize everything that is a TreeNode
return typeof(TreeNode).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// we currently support only writing of JSON
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// we serialize a node by just serializing the _children dictionary
var node = value as TreeNode;
serializer.Serialize(writer, node._children);
}
}
Then you have to decorate your TreeNode class with the JsonConverterAttribute so the serializer knows that it has to use your converter when serializing a TreeNode object.
[JsonConverter(typeof(TreeNodeConverter))]
public class TreeNode : IEnumerable<TreeNode>
{
// ...
}
If you don't use Json.NET, I can't exactly tell you what to do but it might help if you would implement IDictionary instead of IEnumerable so the serializer knows you're dealing with key-value-pairs.

I was preparing data from database for bootstrap treeview. And this worked for me:
public class TreeNode {
public string id { get; private set; }
public string text { get; private set; }
public bool selectable { get; private set; }
public readonly Dictionary<string, TreeNode> nodes = new Dictionary<string, TreeNode>();
[JsonIgnore]
public TreeNode Parent { get; private set; }
public TreeNode(
string id,
string text,
bool selectable
) {
this.id = id;
this.text = text;
this.selectable = selectable;
}
public TreeNode GetChild(string id) {
var child = this.nodes[id];
return child;
}
public void Add(TreeNode item) {
if(item.nodes != null) {
item.nodes.Remove(item.id);
}
item.Parent = this;
this.nodes.Add(item.id, item);
}
}
Buildng tree:
var mapping = new Dictionary<string, TreeNode>(StringComparer.OrdinalIgnoreCase);
foreach(var ln in mx) {
//
var ID = (string)ln[0];
var TEXT = (string)(ln[1]);
var PARENT_ID = (string)(ln[2]);
//
var node = new TreeNode(ID, TEXT, true);
if(!String.IsNullOrEmpty(PARENT_ID) && mapping.ContainsKey(PARENT_ID)) {
var parent = mapping[PARENT_ID];
parent.Add(node);
}
else {
tree.Add(node);
}
mapping.Add(ID, node);
}
var setting = new JsonSerializerSettings {
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
};
// Ignore root element, just childs - tree.nodes.Values
var json = JsonConvert.SerializeObject(tree.nodes.Values, setting);
// Empty dictionary - remove (not needed for valid JSON)
return json.ToString().Replace("\"nodes\": {},", "");

Related

Newtonsoft Json reference loop converter with derived class

Usually, for derived types deserialization, you have to define a custom converter. I mean when there is a base class - Base and others derived from the Base. For that case you have to define a custom convevrter and use some data (maybe an enum) from each derived class to know to which type to convert the data. The problem is when this convertor is called for reference values, for which representation looks like {"$ref":"1"} and the convertor can't obtain any info from this.
Any ideas how to handle this?
Here is the source for better understanding:
public enum ElementType
{
Invalid = 0,
FirstElement,
SecondElement
}
public class SubElement
{
[JsonConstructor]
private SubElement() { }
public SubElement(string name, Element parent)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("message", nameof(name));
}
Name = name;
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
}
[JsonProperty]
public string Name { get; private set; }
[JsonProperty]
public Element Parent { get; private set; }
}
[JsonObject(IsReference = true)]
[JsonConverter(typeof(BaseConverter))]
public abstract class Element
{
[JsonConstructor]
protected Element() { }
public Element(string name, IList<SubElement> subelements)
{
Name = name;
Subelements = subelements;
}
[JsonProperty]
public string Name { get; private set; }
[JsonProperty]
public IList<SubElement> Subelements { get; private set; }
public abstract ElementType Type { get; }
}
public class FirstElement : Element
{
[JsonConstructor]
private FirstElement() { }
public FirstElement(string name, IList<SubElement> subelements) : base(name, subelements) { }
public override ElementType Type => ElementType.FirstElement;
}
public class BaseConverter : CustomCreationConverter<Element>
{
private ElementType elementType;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var obj = JObject.ReadFrom(reader);
elementType = obj["Type"].ToObject<ElementType>();
return base.ReadJson(obj.CreateReader(), objectType, existingValue, serializer);
}
public override Element Create(Type objectType)
{
switch (elementType)
{
case ElementType.FirstElement:
return Activator.CreateInstance(typeof(FirstElement), true) as Element;
default:
throw new NotImplementedException();
}
}
}
class Program
{
static void Main(string[] args)
{
Element firstElement = new FirstElement("FirstElement", new List<SubElement>());
firstElement.Subelements.Add(new SubElement("Subelement1", firstElement));
firstElement.Subelements.Add(new SubElement("Subelement2", firstElement));
string serialized = JsonConvert.SerializeObject(firstElement);
Console.WriteLine(serialized);
Element deserialized = JsonConvert.DeserializeObject<Element>(serialized);
}
}
Serialization output:
{
"$id": "1",
"Type": 1,
"Name": "FirstElement",
"Subelements": [
{
"Name": "Subelement1",
"Parent": {
"$ref": "1"
}
},
{
"Name": "Subelement2",
"Parent": {
"$ref": "1"
}
}
]
}
If someone facing same scenario, I found a workaround by adding a dictionary in BaseConverter class, which maps the elements by their $id when they are created in Create method and, when the current object is an $ref, ReadJson method will return the corresponding mapped element.

Deserialize JSON dictionary-like to object with properties

I'm stuck with an awful API which returns the following JSON:
{
"root":[
{
"row":[
{
"name":"Foo",
"value":"Some value"
},
{
"name":"Bar",
"value":"Some other value"
}, (...)
]
},
{
"row":[
{
"name":"Foo",
"value":"Lorem"
},
{
"name":"Bar",
"value":"Ipsum"
}, (...)
]
}, (...)
]
}
I'd like to use Newtonsoft.Json to deserialize it as a List of C# objects, so that name will match object property and value will be property value. The class would look like this:
class Row
{
public string Foo { get; set; }
public string Bar { get; set; }
(...)
}
Any ideas?
You can use a custom JsonConverter to handle this. Below a simple example that requires some null checks etc, but this gets the idea:
public class RowConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Row[]);
}
public override bool CanWrite => false;
public override bool CanRead => true;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return string.Empty;
}
else if (reader.TokenType == JsonToken.String)
{
return serializer.Deserialize(reader, objectType);
}
else
{
JObject obj = JObject.Load(reader);
var root = obj["root"];
if (root != null)
{
var rows = new List<Row>();
foreach (var item in root)
{
var row = item["row"];
var newRow = new Row();
foreach(var field in row)
{
// better use reflection here to convert name-value to property setter
if (field.Value<string>("name") == "Foo")
{
newRow.Foo = field["value"].Value<string>();
}
if (field.Value<string>("name") == "Bar")
{
newRow.Bar = field["value"].Value<string>();
}
}
rows.Add(newRow);
}
return rows.ToArray();
}
return null;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then on your Row class:
[JsonConverter(typeof(RowConverter))]
public class Row
{
public string Foo { get; set; }
public string Bar { get; set; }
}
If you need to include it in your MVC, in Startup.cs (asp.net core):
services
.AddMvc()
.AddJsonOptions(options => { options.SerializerSettings.Converters.Add(new RowConverter()); });
As an option you can convert it to dictionary for your single "row" property:
var values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
And after create dynamic object:
private static dynamic DictionaryToObject(IDictionary<String, Object> dictionary)
{
var expandoObj = new ExpandoObject();
var expandoObjCollection = (ICollection<KeyValuePair<String, Object>>)expandoObj;
foreach (var keyValuePair in dictionary)
{
expandoObjCollection.Add(keyValuePair);
}
dynamic eoDynamic = expandoObj;
return eoDynamic;
}
private static T DictionaryToObject<T>(IDictionary<String, Object> dictionary) where T : class
{
return DictionaryToObject(dictionary) as T;
}
You can make classes like below then use newtosnsoft
public class root
{
public List<row> row {get;set;}
}
public class row
{
public string name {get;set;}
public string value {get;set;}
}

Deserialize different json property to same model property conditionally

Lets say I have the below json.
{
"allitemscount":2,
"allitems":[
{"itemid":"1","itemname":"one"},
{"itemid":"2","itemname":"two"}],
"customitems":[
{"itemid":"3","itemname":"three"},
{"itemid":"4","itemname":"four"}]
}
and when deserializing this json, it should go to the below C# model.
public class response
{
public int allitemscount;
public List<item> items;
}
public class item
{
public string itemid;
public string itemname;
}
Question:
How to switch between allitems and customitems based on a condition? For eg., if useAllitems is true, allitems from the json to be filled in items and if useCustomItems is true, customitems to be filled in items property. Please help on how to do this.
Using JsonProperty on public List<item> items is allowing to switch between either allitems, but is there a way to deserialize based on the above mentioned condition.
I made it with writing our own ItemConverter which inherited from JsonConverter,
Sample model json that I use in my trying:
{
"allitemscount": 2,
"UseCustomItems": true,
"allitems": [
{
"itemid": "1",
"itemname": "one"
},
{
"itemid": "2",
"itemname": "two"
}
],
"customitems": [
{
"itemid": "3",
"itemname": "three"
},
{
"itemid": "4",
"itemname": "four"
}
]
}
Console application's main method:
static void Main(string[] args)
{
using (StreamReader r = new StreamReader(#"\model.json")) // json path
{
string json = r.ReadToEnd();
var deserializedJson = JsonConvert.DeserializeObject<Result>(json, new ItemConverter());
}
}
Models:
public class Result // main object
{
[JsonProperty("allitemscount")]
public long Allitemscount { get; set; }
public bool UseCustomItems { get; set; }
}
public class ResultA : Result // CustomItems Model
{
[JsonProperty("customitems")]
private List<Item> Items { get; set; }
}
public class ResultB : Result // AllItems Model
{
[JsonProperty("allitems")]
private List<Item> Items { get; set; }
}
public class Item
{
[JsonProperty("itemid")]
public string Itemid { get; set; }
[JsonProperty("itemname")]
public string Itemname { get; set; }
}
And the ItemConverter that we used while deserializing to object:
internal class ItemConverter : JsonConverter
{
private Type currentType;
public override bool CanConvert(Type objectType)
{
return typeof(Item).IsAssignableFrom(objectType) || objectType == typeof(Result);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject item = JObject.Load(reader);
if (item["UseCustomItems"] != null)
{
// save the type for later.
switch (item["UseCustomItems"].Value<bool>())
{
case true:
currentType = typeof(ResultA);
return item.ToObject<ResultA>(); // return result as customitems result
case false:
currentType = typeof(ResultB);
return item.ToObject<ResultB>(); // return result as allitems result
}
return item.ToObject<Result>();
}
// use the last type you read to serialise.
return item.ToObject(currentType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
The result should be like bellowing image
I hope this solution help to you
You can deserialize above json by changing your response class as follows,
public class response{
public int allitemscount;
public List<item> allitems;
public List<item> customitems;
}
then use the following code,
var jsonData = "{\"allitemscount\":2, \"allitems\":[{\"itemid\":\"1\",\"itemname\":\"one\"}, {\"itemid\":\"2\",\"itemname\":\"two\"}],\"customitems\":[{\"itemid\":\"3\",\"itemname\":\"three\"},{\"itemid\":\"4\",\"itemname\":\"four\"}]}";
var data = JsonConvert.DeserializeObject<response>(jsonData);
foreach(var str in data.allitems) {
Console.WriteLine(str.itemid +'-'+str.itemname);
}
foreach(var str in data.customitems) {
Console.WriteLine(str.itemid +'-'+str.itemname);
}

Map properties to a list of objects

I have a JSON data that looks like this:
{
"Item1": {
"Field1": "Val1",
"Field2": "Val2"
},
"Item2": {
"Field1": "Val11",
"Field2": "Val22"
},
....
"ItemN": {
"Field1": "Val1",
"Field2": "Val2"
},
}
I need to deserialize it to a set of classes that look like this:
public class Root
{
public Item Item1;
public Item Item2;
public List<Item> Items; // << all the Items should go here
}
public class Item
{
public string Field1;
public string Field2;
}
How can I make Newtonsoft.Json to map data this way when deserializing?
No need for class Root. I would deserialize to a Dictionary
var dict= JsonConvert.DeserializeObject<Dictionary<string, Item>>(json);
If you need to populate the Items list and the individual Item properties shown in your Root class from the same data in the JSON, then you will need a custom JsonConverter to do it. The following converter should work:
public class RootConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Root);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
var root = new Root();
// populate all known Root properties
serializer.Populate(obj.CreateReader(), root);
// populate Items list with all objects that can be converted to an Item
root.Items = obj.Properties()
.Where(p => p.Value.Type == JTokenType.Object &&
(p.Value["Field1"] != null || p.Value["Field2"] != null))
.Select(p => p.Value.ToObject<Item>())
.ToList();
return root;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then to use it, add a [JsonConverter] attribute to your Root class like this:
[JsonConverter(typeof(RootConverter))]
public class Root
{
public Item Item1 { get; set; }
public Item Item2 { get; set; }
public List<Item> Items; // << all the Items should go here
}
Fiddle: https://dotnetfiddle.net/aADhzw
From #L.B post: If you need work with Root object you can try this way:
public class Root : Dictionary<string, Item>
{
public List<Item> Items => this.Values.ToList();
}
Then you can deserialize JSON to Root object:
var root = JsonConvert.DeserializeObject<Root>(json);
Use:
public class Root
{
public List<Item> Items {get;set;}
}
public class Item
{
public string Field1 {get;set;}
public string Field2; {get;set;}
}
As the classes, and call:
Root foobar = new JavaScriptSerializer().Deserialize<Root>(result);
To deserialise.

Deserialize JSON to type containing reference to another deserialized object in C#

I have a following model:
public interface IEntity
{
string Id { get; }
}
public class EntityParent : IEntity
{
public string Id { get; }
public EntityChild EntityChild { get; }
[JsonConstructor]
public EntityParent(string id, EntityChild entityChild)
{
Id = id;
EntityChild = entityChild;
}
}
public class EntityChild : IEntity
{
public string Id { get; }
public int Age { get; }
[JsonConstructor]
public EntityChild(string id, int age)
{
Id = id;
Age = age;
}
}
Next I have some JSON that I need to deserialize to collections of the above types:
{
"Children":
[
{
"Id" : "Billy",
"Age" : 42
}
],
"Parents" :
[
{
"Id" : "William",
"EntityChild" : "Billy"
}
]
}
Eventually I want to have a list of EntityChildren and a list of EntityParents that will (optionally) contain references to objects in the first list , or at least references to instances of EntityChild. I have attempted to write a custom JsonConverter (I am using Newtonsoft.Json 9.0.1 NuGet package), where in the ReadJson() method I am looking for a child with specific Id, like so:
public class ParentConverter<TEntity> : JsonConverter where TEntity : IEntity
{
private readonly IEnumerable<TEntity> _children;
public ParentConverter(IEnumerable<TEntity> children)
{
_children = children;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
TEntity target = _children.FirstOrDefault(d => d.Id == jObject["Id"].ToString());
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override bool CanConvert(Type objectType)
{
return typeof(TEntity).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Here's a simple test:
public class JsonTest
{
const string Json = #"
{
""Children"": [
{
""Id"" : ""Billy"",
""Age"" : 42
}
],
""Parents"" : [
{
""Id"" : ""William"",
""EntityChild"" : ""Billy""
}
]
}";
public static void Main()
{
JObject jObject = JObject.Parse(Json);
var children =
JsonConvert.DeserializeObject<List<EntityChild>>(jObject["Children"].ToString());
var parents =
JsonConvert.DeserializeObject<List<EntityParent>>(jObject["Parents"].ToString(),
new ParentConverter<EntityChild>(children));
}
}
children are deserialized properly, but parents throw a JsonReaderException when attempting to call JObject.Load(reader); in ReadJson(), saying "Error reading JObject from JsonReader. Current JsonReader item is not an object: String. Path '[0].EntityChild'."
Does anyone know how I should go about it? Thanks in advance.
Edit: Updated the EntityChild with extra property to emphasize that the property on EntityParent has to be of EntityChild type, and not a string.
Entity parent should be as below:
public class EntityParent : IEntity
{
public string Id { get; }
public string EntityChild { get; }
[JsonConstructor]
public EntityParent(string id, string entityChild)
{
Id = id;
EntityChild = entityChild;
}
}
And in main() change as below:
var parents = JsonConvert.DeserializeObject<List<EntityParent>>(jObject["Parents"].ToString());
and it works.
The following is sort of a workaround using LINQ, where I basically join the parent JObjects with children JObjects to build the parent and the child:
public static void Main()
{
JObject jObject = JObject.Parse(Json);
IEnumerable<EntityParent> parents =
from parent in jObject["Parents"]
join child in jObject["Children"] on parent["EntityChild"] equals child["Id"]
select
new EntityParent(
parent["Id"].ToString(),
new EntityChild(
child["Id"].ToString(),
child["Age"].ToObject<int>()));
}
If children list would exist already, then the join could be performed on that list instead, like so:
public static void Main()
{
JObject jObject = JObject.Parse(Json);
var children =
JsonConvert.DeserializeObject<List<EntityChild>>(jObject["Children"].ToString());
IEnumerable<EntityParent> parents =
from parent in jObject["Parents"]
join child in children on parent["EntityChild"] equals child.Id
select new EntityParent(parent["Id"].ToString(), child);
}

Categories

Resources