In general your conclusion about Json.NET is correct. The original JSON proposal states:
An object is an unordered set of name/value pairs.
And the Current IETF JSON standard states:
An object is an unordered collection of zero or more name/value pairs, where a name is a string and a value is a string, number, boolean, null, object, or array.
An object whose names are all unique is interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings.
Json.NET supports these standards. Thus, while it allows for applications to control property order via attributes when serializing (either through [JsonProperty(Order = X)]
; or through [DataMember(Order = X)]
, which works because Json.NET supports data contract attributes), when deserializing property order usually does not matter.
However, there are some exceptions. Those are:
Polymorpmic "$type"
properties emitted when TypeNameHandling
is enabled must appear first in the object. This limitation, however, can be removed by setting MetadataPropertyHandling.ReadAhead
. See here for details.
Custom JsonConverters have access to the incoming token stream so can in theory behave differently depending on the order of properties.
For instance, when deserializing a DataTable
, Json.NET's built-in DataTableConverter
will create columns in the order that they are encountered in the JSON file.
It's also possible to write converters that assume a certain property order and thus are subtly broken. For instance, consider the following:
public struct Vector2D
{
public readonly double X;
public readonly double Y;
public Vector2D(double x, double y)
{
this.X = x;
this.Y = y;
}
}
public class Vector2DConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Vector2D);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
throw new JsonSerializationException();
reader.Read(); // StartObject
reader.Read(); // X
var x = serializer.Deserialize<double>(reader);
reader.Read(); // Consume value
reader.Read(); // Y
var y = serializer.Deserialize<double>(reader);
reader.Read(); // Consume value
reader.Read(); // EndObject
return new Vector2D(x, y);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var vec = (Vector2D)value;
writer.WriteStartObject();
writer.WritePropertyName("X");
writer.WriteValue(vec.X);
writer.WritePropertyName("Y");
writer.WriteValue(vec.Y);
writer.WriteEndObject();
}
}
The Vector2DConverter
assumes that the properties X
and Y
come in a certain order, rather than checking the property names and deserializing accordingly. While the converter can read what it writes, its order sensitivity fails to fully conform to the JSON standard. Users of such a converter might run into trouble e.g. if the JSON is temporarily cached in a Dictionary<string, object>
before final conversion, since the order of elements in the c# dictionary is non-deterministic.
None of Json.NET's built-in converters should be broken in this manner, but third-party converters might possibly be.
Update: Json.NET vs data contract serialization and member order
By default WCF uses DataContractSerializer
for XML and DataContractJsonSerializer
for JSON. DataContractSerializer
is sensitive to element order when deserializing. This cannot be disabled; see here, here or here.
DataContractJsonSerializer
on the other hand respects data member order when serializing but is insensitive to property order when deserializing as is required by the standard - with one exception. If the JSON contains a "type hint" to preserve polymorphic type identity when sending JSON for polymorphic objects over the wire, for instance:
{"__type":"Circle:#MyApp.Shapes","x":50,"y":70,"radius":10}
Then the "__type"
property must appear first in the object, as is stated in the docs:
Note that the type hint must appear first in the JSON representation. This is the only case where order of key/value pairs is important in JSON processing.
From experience I also have found that there must be no spacing between the {
and the "__type"
property name.
Json.NET also supports data contract attributes, as is explained in DataContract and DataMember Attributes. Its behavior with respect to property ordering is the same as DataContractJsonSerializer
(although it does use a different format for type hinting) in that it respects the ordering when serializing but is insensitive to ordering when deserializing.