Jesse Jesse - 4 months ago 20
C# Question

Serialization pattern to maintain back-references

So if I have a one-to-many relationship between two objects, I can only serialize one way, otherwise I get a cyclical error when using Json.NET.

I want to serialize the objects both ways, so I can navigate up and down the object.

Reading Json.NET's documenation, I came upon the ability to conditionally serialize a C# poco property by adding a shouldSerialize method suffixed by the property name..

Take a manager and and employee class. One manager has many employees. I want employees to backreference managers, but I only want managers to reference employees if the manager is not a property of an employee.

{
manager: {
managerId: 1,
attr1: 'blah',

employees: [
{
employeeId:1,
manager: {
manager id: 1,
attr1: 'blah',
employees: **null**
}
]
}


Is this possible to do? What's the best way to do it?

Answer

If you use ShouldSerialize this way, then your backreferences won't be backreferences. Employee.Manager will point to a different Manager, not the one that contains the employee in its Employees property.

You should use JsonSerializerSettings.PreserveReferencesHandling instead, which was designed speifically to make cross-links possible and to avoid object duplication.

class Program
{
    private static void Main ()
    {
        var m = new Manager { Attr = "Attr1" };
        var e = new Employee { Attr = "Attr1", Manager = m };
        m.Employees = new[] { e };
        Console.WriteLine(JsonConvert.SerializeObject(m,
            new JsonSerializerSettings {
                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                Formatting = Formatting.Indented,
            }));
        Console.ReadKey();
    }
}

class Manager
{
    public Employee[] Employees;
    public string Attr;
}

class Employee
{
    public Manager Manager;
    public string Attr;
}

This code will output the following JSON:

{
  "$id": "1",
  "Employees": [
    {
      "$id": "2",
      "Manager": {
        "$ref": "1"
      },
      "Attr": "Attr1"
    }
  ],
  "Attr": "Attr1"
}

Note that all objects now have $id property and when an object appears a second time in the object graph, a reference to it is inserted instead of the object: { "$ref": "1" }.