2

Closed

Output of generated JSON is undeterministic

description

The order in which a type's members are serialized to JSON is based on the order in which the .NET runtime returns them. With the current C# 5.0 compiler and the current 4.5 version of the CLR this order is deterministic, but there is guarantee that this will change in the future, as Eric Lippert explains clearly.

For the correctness of the generated JSON, the order in which those members are presented does not matter, but if users depend on a certain order (as some unit tests of the JSON.NET codebase already do) their application might fail in the future.

Since JSON.NET is an amazingly powerful tool that can serialize about anything without a problem, it becomes rather interesting to use JSON.NET for creating keys. For instance, consider a DTO message that holds the data for a query to be executed. This message can be serialized to JSON and the resulting string can be used as key in a cache. If this key is hashed it could even be used as a file name where the content of the file is the serialized result of the query. This would effectively enable the use of those DTO messages as key in a cache, without the need for each of them to override equality and even allows the cache to outlive the application instance.

With the current version of the C# compiler and the framework this works out rather well, but this will break in future versions of the platform, where the order in which members are returned might change every time the application is restarted.

What I'd like is that either:
  • JSON.NET guarantees the order of the serialized member (for instance by sorting them by name in the DefaultContractResolver.GetSerializableMembers method)
  • Allow users to extend the framework in a way that they can force a particular order in which the members are serialized.
  • Let JSON.NET explicitly state that the order of the members is undeterministic and can change every time the application is recompiled or even restarted. (this is of course the least attractive option).
Closed Dec 29, 2013 at 6:02 AM by JamesNK
I'm happy with the current features

comments

JamesNK wrote Dec 26, 2013 at 1:13 AM

JsonPropertyAttribute has an Order setting and there is nothing stopping someone to override GetSerializableMembers themselves if they want a custom global order.

dot_NET_Junkie wrote Dec 26, 2013 at 1:12 PM

Having to mark all properties with the JSonPropertyAttribute of course sucks, since that means that you would have to go through the complete code base and place attributes everywhere. Overriding the GetSerializableMembers however is probably the right solution.

This is probably the way to do it:
public class DeterministicContractResolver : DefaultContractResolver {
    protected override List<MemberInfo> GetSerializableMembers(Type objectType) {
        return base.GetSerializableMembers(objectType).OrderBy(m => m.Name).ToList();
    }
}
This custom DeterministicContractResolver can be registered as follows:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
    ContractResolver = new DeterministicContractResolver()
};
That does it!