Sjoerd222888 Sjoerd222888 - 6 months ago 20
JSON Question

Json.Net issue with order of reference properties in JSON

The order of properties is not defined so I would expect the order of the properties in JSON also not to be defined. However I find Newtonsoft.Json to expect a certain order when having references (I am using

PreserveReferencesHandling = PreserveReferencesHandling.All
). It expects the
$id
property to be the first occurring property in JSON.

I have come to this conclusion with the following test

string cyclicJson1 = "{\"FirstChild\":{\"OtherChild\":{\"OtherChild\":{\"$ref\":\"1\"},\"Parent\":{\"$ref\":\"0\"},\"$id\":\"2\"},\"Parent\":{\"$ref\":\"0\"},\"$id\":\"1\"},\"SecondChild\":{\"$ref\":\"2\"},\"$id\":\"0\"}\"";


cannot be deserilized correctly (some reference are
null
which should not) by Newtonsoft.Json where as the following can:

string cyclicJson2 = "{\"$id\": \"0\",\"FirstChild\": {\"$id\": \"1\",\"OtherChild\": {\"$id\": \"2\",\"OtherChild\": {\"$ref\": \"1\"},\"Parent\": {\"$ref\": \"0\"}},\"Parent\": {\"$ref\": \"0\"},},\"SecondChild\": {\"$ref\": \"2\"}}";


The only difference being that I manually moved the
$id
property forward such that it is the first element for each object.

The classes are defined as follows:

class CycleTestParent
{
public CycleTestChild FirstChild { get; set; }
public CycleTestChild SecondChild { get; set; }
public CycleTestParent()
{
FirstChild = new CycleTestChild();
SecondChild = new CycleTestChild();
}
}

private class CycleTestChild
{
public CycleTestParent Parent { get; set; }
public CycleTestChild OtherChild { get; set; }
}





Is there a way that I can work with Newtonsoft.Json without having to assume that the
$id
property always occurs first? Is there another way than resorting the JSON string manually?

Evk Evk
Answer

What you need here is to set MetadataPropertyHandling to MetadataPropertyHandling.ReadAhead:

JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{                    
    MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead
};

By default, for perfomance reasons, metadata properties are expected to be located at the beginning of object JSON. With this property you can change parser behaviour to look them in any place inside object JSON.