What is the difference between PreserveReferencesHandling and ReferenceLoopHandling in Json.Net?

I am considering one example WebAPI application that has this code:

json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects; 

and the other with this code:

 json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; 

None of them explain why everyone is chosen. I am very new to WebAPI, so someone can help by explaining in simple words what the differences are and why I might need to use one over the other.

+48
json c # asp.net-web-api
May 4 '14 at 8:04
source share
1 answer

These settings are best explained with an example. Let me say that we want to represent the hierarchy of employees in the company. Therefore, we make a simple class as follows:

 class Employee { public string Name { get; set; } public List<Employee> Subordinates { get; set; } } 

This is a small company with three employees: Angela, Bob and Charles. Angela is the boss, and Bob and Charles are her subordinates. Let me customize the data to describe this relationship:

 Employee angela = new Employee { Name = "Angela Anderson" }; Employee bob = new Employee { Name = "Bob Brown" }; Employee charles = new Employee { Name = "Charles Cooper" }; angela.Subordinates = new List<Employee> { bob, charles }; List<Employee> employees = new List<Employee> { angela, bob, charles }; 

If we serialize the list of employees in JSON ...

 string json = JsonConvert.SerializeObject(employees, Formatting.Indented); Console.WriteLine(json); 

... we get this result:

 [ { "Name": "Angela Anderson", "Subordinates": [ { "Name": "Bob Brown", "Subordinates": null }, { "Name": "Charles Cooper", "Subordinates": null } ] }, { "Name": "Bob Brown", "Subordinates": null }, { "Name": "Charles Cooper", "Subordinates": null } ] 

So far so good. However, you will notice that the information for Bob and Charles is repeated in JSON because the objects that represent them refer to both the main list of employees and the list of Angela's subordinates. Maybe now OK.

Now suppose we would also like to be able to keep track of each Employee leader in addition to our subordinates. Therefore, we are changing our Employee model to add the Supervisor property ...

 class Employee { public string Name { get; set; } public Employee Supervisor { get; set; } public List<Employee> Subordinates { get; set; } } 

... and add a couple more lines to our installation code to show that Charles and Bob report to Angela:

 Employee angela = new Employee { Name = "Angela Anderson" }; Employee bob = new Employee { Name = "Bob Brown" }; Employee charles = new Employee { Name = "Charles Cooper" }; angela.Subordinates = new List<Employee> { bob, charles }; bob.Supervisor = angela; // added this line charles.Supervisor = angela; // added this line List<Employee> employees = new List<Employee> { angela, bob, charles }; 

But now we have a little problem. Since the object graph has reference loops (for example, angela links are bob , and bob links are angela ), we will get a JsonSerializationException when we try to serialize the list of employees. One way around this problem is to set ReferenceLoopHandling in Ignore as follows:

 JsonSerializerSettings settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, Formatting = Formatting.Indented }; string json = JsonConvert.SerializeObject(employees, settings); 

Using this setting, we get the following JSON:

 [ { "Name": "Angela Anderson", "Supervisor": null, "Subordinates": [ { "Name": "Bob Brown", "Subordinates": null }, { "Name": "Charles Cooper", "Subordinates": null } ] }, { "Name": "Bob Brown", "Supervisor": { "Name": "Angela Anderson", "Supervisor": null, "Subordinates": [ { "Name": "Charles Cooper", "Subordinates": null } ] }, "Subordinates": null }, { "Name": "Charles Cooper", "Supervisor": { "Name": "Angela Anderson", "Supervisor": null, "Subordinates": [ { "Name": "Bob Brown", "Subordinates": null } ] }, "Subordinates": null } ] 

If you learn JSON, it should be clear what this parameter does: at any time when the serializer encounters a reference to an object that is already in the process of serialization, it simply skips this element. (This prevents the serializer from getting into an infinite loop.) You can see that in the list of Angela's subordinates at the top of the JSON, neither Bob nor Charles show the supervisor. At the bottom of the JSON, Bob and Charles show Angela as their leader, but notice that her list of subordinates at this point does not include both Bob and Charles.

While you can work with this JSON and, perhaps, even restore the original hierarchy of objects from it with some work, this is clearly not optimal. We can exclude duplicate information in JSON, while maintaining object references, using the PreserveReferencesHandling parameter instead:

 JsonSerializerSettings settings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects, Formatting = Formatting.Indented }; string json = JsonConvert.SerializeObject(employees, settings); 

Now we get the following JSON:

 [ { "$id": "1", "Name": "Angela Anderson", "Supervisor": null, "Subordinates": [ { "$id": "2", "Name": "Bob Brown", "Supervisor": { "$ref": "1" }, "Subordinates": null }, { "$id": "3", "Name": "Charles Cooper", "Supervisor": { "$ref": "1" }, "Subordinates": null } ] }, { "$ref": "2" }, { "$ref": "3" } ] 

Note that now each object is assigned a sequential $id value in JSON. The first time an object appears, it is completely serialized, and subsequent links are replaced with the special property $ref , which refers to the original object with the corresponding $id . With this setting, JSON is much more concise and can be deserialized back to the original object hierarchy without additional work if you use a library that understands the $id and $ref notation released by the Json.Net/Web API.

So why would you choose one option or another? Of course, it depends on your needs. If JSON will be consumed by a client that does not understand the $id / $ref format, and it can carry incomplete data in places, you should use ReferenceLoopHandling.Ignore . If you are looking for a more compact JSON, and you will use the Json.Net or Web API (or other compatible library) to deserialize the data, you can use PreserveReferencesHandling.Objects . If your data is directed acyclic graphs without duplicate links, you do not need to configure.

+114
May 04 '14 at 20:25
source share



All Articles