1

In a farm where there is a number of cows and a number of farmers who are responsible for some of those cows, PreserveReferencesHandling reduces the size of the resulting json dramatically (45% in this case).

public class Farm
{
    public List<Cow> cows;
    public List<Farmer> farmers;
    public List<List<Farmer>> teams;
    ...
}
public class Cow
{
    //Some properties...
}
public class Farmer
{
    public List<Cow> cows;
    ...
}

If PreserveReferencesHandling is turned off, this may lead to tons of cows being needlessly duplicated in the json, which, depending on whatever implementation there might be, may in fact break the program e.g. because of equality checks failing.

For example: Without PreserveReferencesHandling

// characters: 985
{
  "cows": [
    {"name":"SmallCow"},
    {"name":"Cow"},
    {"name":"BigCow"}
  ],
  "farmers":[
    {"name":"Joe", "cows":[{"name":"SmallCow"}, {"name":"Cow"}]},
    {"name":"Chris", "cows":[{"name":"SmallCow"}, {"name":"BigCow"}]},
    {"name":"Chris Junior", "cows":[{"name":"SmallCow"}, {"name":"BigCow"}]},
    {"name":"Bob", "cows":[{"name":"SmallCow"}, {"name":"Cow"}, {"name":"BigCow"}]}
  ],
  "teams": [
    [{"name":"Joe", "cows":[{"name":"SmallCow"}, {"name":"Cow"}]}, {"name":"Chris", "cows":[{"name":"SmallCow"}, {"name":"BigCow"}]}],
    [{"name":"Chris Junior", "cows":[{"name":"SmallCow"}, {"name":"BigCow"}]}, {"name":"Chris", "cows":[{"name":"SmallCow"}, {"name":"BigCow"}]}],
    [{"name":"Joe", "cows":[{"name":"SmallCow"}, {"name":"Cow"}]},
    {"name":"Chris", "cows":[{"name":"SmallCow"}, {"name":"BigCow"}]},
    {"name":"Chris Junior", "cows":[{"name":"SmallCow"}, {"name":"BigCow"}]},
    {"name":"Bob", "cows":[{"name":"SmallCow"}, {"name":"Cow"}, {"name":"BigCow"}]}]
  ]
}

With PreserveReferencesHandling.Objects

// characters: 547
{ 
  "cows": [
    {"$id": 0, "name":"SmallCow"},
    {"$id": 1,"name":"Cow"},
    {"$id": 2,"name":"BigCow"}
  ],
  "farmers":[
    {"$id": 3, "name":"Joe", "cows":[{"$ref": 0}, {"$ref": 1}]},
    {"$id": 4, "name":"Chris", "cows":[{"$ref": 0}, {"$ref": 2}]},
    {"$id": 5, "name":"Chris Junior", "cows":[{"$ref": 0}, {"$ref": 2}]},
    {"$id": 6, "name":"Bob", "cows":[{"$ref": 0}, {"$ref": 1}, {"$ref": 2}]}
  ],
  "teams": [
    [{"$ref" : 3}, {"$ref" : 4}],
    [{"$ref" : 5}, {"$ref" : 4}],
    [{"$ref" : 3}, {"$ref" : 4}, {"$ref" : 5}, {"$ref" : 6}]
  ]
}

This example is somewhat engineered to show the difference, but I would assume this memory saving would only get more pronounced the more complex and interconnected a data system becomes.

What are the pros and cons of PreserveReferenceHandling? Which setting is most useful, PreserveReferenceHandling.Objects, PreserveReferenceHandling.Arrays or PreserveReferenceHandling.All?

TheForgot3n1
  • 212
  • 2
  • 11

1 Answers1

1

Why would you not use PreserveReferencesHandling in Json.NET?

Some downsides of the PreserveReferencesHandling behaviour include:

  • It generates "weird" looking JSON that other JSON parsers will struggle to deserialise (this is particularly an issue when deserialising it in different languages).
  • There may be an explicit desire to not have reference handling (e.g. the consumer specifically wants each instance to be unique, not references to the same value).

If you want performance / conciseness then some form of binary format (e.g. protobuf) makes more sense. JSON has benefits of being human readable and largely interoperable across languages. Using PreserveReferencesHandling dampens both benefits considerably.

The very nature of software development is tradeoffs. Are there benefits to using PreserveReferencesHandling? Sure there are - which is why it exists. But the authors of the library, rightly, optimised for interoperability while providing the option if you need it.

In addition, the use of PreserveReferencesHandling does not always "play well" with other Json.NET features, and it may slow some serialising / deserialising due to the extra effort of tracking and comparing the object references. Profiling would be necessary to see what performance looks like for you.

mjwills
  • 23,389
  • 6
  • 40
  • 63
  • For the record, I've never used the option and I've never seen it used in the wild (except in library / framework code). – mjwills Jun 01 '20 at 06:06