In a project, I had a tree of objects, each node of the tree having some attributes, a list of children and also in some cases a reference to some other object in the tree. I needed to serialize it in JSON format and send it to a JavaScript client.
In order to serialize it I used the JavaScriptSerializer. It basically serializes in a recursive way all public properties of the provided object. Here a short example:
using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;
namespace Playground
{
public static class JsonTest
{
class MyObject
{
public MyObject()
{
Children = new List<MyObject>();
}
public string Name { get; set; }
public Guid Guid { get; set; }
public IList<MyObject> Children { get; set; }
public MyObject BestFriend { get; set; }
}
public static void Main()
{
var root = new MyObject {Name = "Root", Guid = Guid.NewGuid()};
var object1 = new MyObject { Name = "Child1", Guid = Guid.NewGuid() };
var object2 = new MyObject { Name = "Child2", Guid = Guid.NewGuid() };
var object3 = new MyObject { Name = "Child3", Guid = Guid.NewGuid() };
var object4 = new MyObject { Name = "Child4", Guid = Guid.NewGuid() };
var object5 = new MyObject { Name = "Child5", Guid = Guid.NewGuid() };
var object6 = new MyObject { Name = "Child6", Guid = Guid.NewGuid() };
object1.Children.Add(object3);
object1.Children.Add(object4);
object2.Children.Add(object5);
object2.Children.Add(object6);
root.Children.Add(object1);
root.Children.Add(object2);
string s = new JavaScriptSerializer().Serialize(root);
Console.WriteLine(s);
}
}
}
This all works fine until you add a reference to a node further up in the tree to the BestFriend property:
public static void Main()
{
var root = new MyObject {Name = "Root", Guid = Guid.NewGuid()};
var object1 = new MyObject { Name = "Child1", Guid = Guid.NewGuid() };
var object2 = new MyObject { Name = "Child2", Guid = Guid.NewGuid() };
var object3 = new MyObject { Name = "Child3", Guid = Guid.NewGuid() };
var object4 = new MyObject { Name = "Child4", Guid = Guid.NewGuid() };
var object5 = new MyObject { Name = "Child5", Guid = Guid.NewGuid() };
var object6 = new MyObject { Name = "Child6", Guid = Guid.NewGuid() };
object1.Children.Add(object3);
object1.Children.Add(object4);
object2.Children.Add(object5);
object2.Children.Add(object6);
root.Children.Add(object1);
root.Children.Add(object2);
object3.BestFriend = object1;
string s = new JavaScriptSerializer().Serialize(root);
Console.WriteLine(s);
}
When running it, you will get a InvalidOperationException with the following message:
A circular reference was detected while serializing an object of type ‘Playground.JsonTest+MyObject’.
The reason is that while walking through the tree we came to object3 and followed the reference back to object1 which would then lead us back to object3 and so on. So the solution is would be not to follow the BestFriend reference while serializing. In my case I didn’t even need to keep this reference in the JSON structure.
In order to do it you need to create a new object for each MyObject containing the properties we do need in the JSON structure and not containing the BestFriend property e.g.:
new {
Name = o.Name,
Guid = o.Guid,
Children = o.Children
};
The problem is that if you use the Children property you’ll be back with the BestFriend problem. So we need to convert the MyObject to a new type and in this new type still have the children but not as MyObjects but as the new type. So basically we need to recursively convert all MyObjects.
Sounds like a job for Linq. Unfortunately Linq doesn’t provide any extension for working in such a recursive way. If you knew you had only three levels like in our example above you could do something like this:
new {
Name = o.Name,
Guid = o.Guid,
Children = o.Children.Select(new {
Name = o.Name,
Guid = o.Guid,
Children = o.Children.Select(new {
Name = o.Name,
Guid = o.Guid
}
}
};
But if the depth of the tree is not limited, you cannot do it this way. For something like this, you need to have a recursive method or function converting the nodes. Since in my case I couldn’t use a recursive methods (because the code was entered by a user and was dynamically compiled in a method), I had to go for a method delegate and a lambda expression. Since I couldn’t define a new class either I also needed a dynamic type. Here my first try at it:
Func<MyObject, object> f = o =>
{
dynamic t = new
{
Name = o.Name,
Guid = o.Guid,
Children = o.Children.Select(e => f(e))
};
return t;
};
var r = f(root);
string s = new JavaScriptSerializer().Serialize(r);
If you try this, you will see it won’t compile and will show the following error:
Use of unassigned local variable ‘f’
The problem is that we’re defining f and using it in one statement. So the solution is obvious: split the declaration and assignment:
Func<MyObject, object> f = null;
f = o =>
{
dynamic t = new
{
Name = o.Name,
Guid = o.Guid,
Children = o.Children.Select(e => f(e))
};
return t;
};
With this I do get a JSON structure without any problems related to circular references. Here the whole source code for your reference:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Script.Serialization;
namespace Playground
{
public static class JsonTest
{
class MyObject
{
public MyObject()
{
Children = new List<MyObject>();
}
public string Name { get; set; }
public Guid Guid { get; set; }
public IList<MyObject> Children { get; set; }
public MyObject BestFriend { get; set; }
}
public static void Main()
{
var root = new MyObject {Name = "Root", Guid = Guid.NewGuid()};
var object1 = new MyObject { Name = "Child1", Guid = Guid.NewGuid() };
var object2 = new MyObject { Name = "Child2", Guid = Guid.NewGuid() };
var object3 = new MyObject { Name = "Child3", Guid = Guid.NewGuid() };
var object4 = new MyObject { Name = "Child4", Guid = Guid.NewGuid() };
var object5 = new MyObject { Name = "Child5", Guid = Guid.NewGuid() };
var object6 = new MyObject { Name = "Child6", Guid = Guid.NewGuid() };
object1.Children.Add(object3);
object1.Children.Add(object4);
object2.Children.Add(object5);
object2.Children.Add(object6);
root.Children.Add(object1);
root.Children.Add(object2);
object3.BestFriend = object1;
Func<MyObject, object> f = null;
f = o =>
{
dynamic t = new
{
Name = o.Name,
Guid = o.Guid,
Children = o.Children.Select(e => f(e))
};
return t;
};
var r = f(root);
string s = new JavaScriptSerializer().Serialize(r);
Console.WriteLine(s);
}
}
}
Note that the Mono compiler seems to have issues with it but it works fine using the Microsoft CSharp compiler.
When using JSON.NET (also known as Newtonsoft.Json), there is a useful setting called ReferenceLoopHandling. If you set it to ReferenceLoopHandling.Ignore, the serializer will only serialize objects once. If an object is encountered a second time, it will not be serialized again. Here’s how to use this setting:
string s = JsonConvert.SerializeObject(root, Formatting.None,
new JsonSerializerSettings {ReferenceLoopHandling = ReferenceLoopHandling.Ignore});
There are two additional options for ReferenceLoopHandling:
- ReferenceLoopHandling.Error: This is the default behavior. You get an Exception when a circular reference is detected.
- ReferenceLoopHandling.Serialize: This will force the serializer to serialize everything even if a circular reference is detected. This seems to be mostly useful in cases where you have nested object but know there won’t be an infinite loop. But frankly I’d not recommend using it at all.
If you are using the standard JavaScriptSerializer, you should consider moving the JSON.NET. It’s fast, reliable and provide nice additional features like this one. I have moved to JSON.NET for all my projects and have never looked back.
Also if you are using the Entity framework, it might be worth a try switching off the automatic proxy generation on your DB context using:
dbContext.Configuration.ProxyCreationEnabled = false;