3

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?

Athari
  • 33,702
  • 16
  • 105
  • 146
Jesse
  • 1,673
  • 1
  • 16
  • 22

1 Answers1

2

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" }.

Athari
  • 33,702
  • 16
  • 105
  • 146
  • so if my task is to determine the manager of an employee, I would iterate over the managers and match up the $ref property with the $id property. I was pretty much doing this already, but instead I was going one level deeper and looking at the all the employees of each manager and determining if the manager contained the employee I was looking at. This way you have suggested is more sane and seems to be the most reasonable solution given the constraints of json. Out of curiosity, are there any serialization formats support back-references? – Jesse Aug 30 '13 at 19:52
  • 1
    @Jesse XML supports id/idref natively, [DataContractSerializer](http://stackoverflow.com/questions/1617528/net-xml-serialization-storing-reference-instead-of-object-copy) supports this feature. YAML supports references, but they're more like copy-pasting than actual referencing, and its libraries are horrible. JSON does not support references natively, but it can be easily extended (like it was done in JSON.NET). Also see Wikipedia page: http://en.wikipedia.org/wiki/Comparison_of_data_serialization_formats (I don't know most of the formats, so I can't comment on them.) – Athari Aug 30 '13 at 22:17