152
votes

Is there a way to specify the order of fields in a serialized JSON object using JSON.NET?

It would be sufficient to specify that a single field always appear first.

15
i think he's probably interested in showing the ID field (or similar) first, and then all the other fields. this is friendlier for end-users than looking for it after fields that begin with A..I - Michael Bahig
JSON properties are defined to be unordered. I think it's absolutely fine to force a particular OUTPUT order during serialization (for the sake of eyeballing the JSON perhaps), but it would be a bad decision to create a DEPENDENCY on any particular order on deserialization. - DaBlick
A couple of valid reasons: (1) faking a "$type" property which must be the first property in the JSON, (2) trying to generate JSON that compresses as much as possible - Stephen Chung
Another reason might be (3) a canonical representation which uses JSON syntax - the same object must be guaranteed to produce the same JSON string. A deterministic order of the attributes is a necessary prerequisite for this. - MarkusSchaber
Kevin, can you update the accepted answer on this question? - Millie Smith

15 Answers

270
votes

The supported way is to use the JsonProperty attribute on the class properties that you want to set the order for. Read the JsonPropertyAttribute order documentation for more information.

Pass the JsonProperty an Order value and the serializer will take care of the rest.

 [JsonProperty(Order = 1)]

This is very similar to the

 DataMember(Order = 1) 

of the System.Runtime.Serialization days.

Here is an important note from @kevin-babcock

... setting the order to 1 will only work if you set an order greater than 1 on all other properties. By default any property without an Order setting will be given an order of -1. So you must either give all serialized properties and order, or set your first item to -2

134
votes

You can actually control the order by implementing IContractResolver or overriding the DefaultContractResolver's CreateProperties method.

Here's an example of my simple implementation of IContractResolver which orders the properties alphabetically:

public class OrderedContractResolver : DefaultContractResolver
{
    protected override System.Collections.Generic.IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
    {
        return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
    }
}

And then set the settings and serialize the object, and the JSON fields will be in alphabetical order:

var settings = new JsonSerializerSettings()
{
    ContractResolver = new OrderedContractResolver()
};

var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
21
votes

In my case Mattias' answer didn't work. The CreateProperties method was never called.

After some debugging of Newtonsoft.Json internals, I came up with another solution.

public class JsonUtility
{
    public static string NormalizeJsonString(string json)
    {
        // Parse json string into JObject.
        var parsedObject = JObject.Parse(json);

        // Sort properties of JObject.
        var normalizedObject = SortPropertiesAlphabetically(parsedObject);

        // Serialize JObject .
        return JsonConvert.SerializeObject(normalizedObject);
    }

    private static JObject SortPropertiesAlphabetically(JObject original)
    {
        var result = new JObject();

        foreach (var property in original.Properties().ToList().OrderBy(p => p.Name))
        {
            var value = property.Value as JObject;

            if (value != null)
            {
                value = SortPropertiesAlphabetically(value);
                result.Add(property.Name, value);
            }
            else
            {
                result.Add(property.Name, property.Value);
            }
        }

        return result;
    }
}
16
votes

In my case niaher's solution did not work because it didn't handle objects in arrays.

Based on his solution this is what I came up with

public static class JsonUtility
{
    public static string NormalizeJsonString(string json)
    {
        JToken parsed = JToken.Parse(json);

        JToken normalized = NormalizeToken(parsed);

        return JsonConvert.SerializeObject(normalized);
    }

    private static JToken NormalizeToken(JToken token)
    {
        JObject o;
        JArray array;
        if ((o = token as JObject) != null)
        {
            List<JProperty> orderedProperties = new List<JProperty>(o.Properties());
            orderedProperties.Sort(delegate(JProperty x, JProperty y) { return x.Name.CompareTo(y.Name); });
            JObject normalized = new JObject();
            foreach (JProperty property in orderedProperties)
            {
                normalized.Add(property.Name, NormalizeToken(property.Value));
            }
            return normalized;
        }
        else if ((array = token as JArray) != null)
        {
            for (int i = 0; i < array.Count; i++)
            {
                array[i] = NormalizeToken(array[i]);
            }
            return array;
        }
        else
        {
            return token;
        }
    }
}
4
votes

This will work for normal classes, dictionaries and ExpandoObject (dynamic object) as well.

class OrderedPropertiesContractResolver : DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
        {
            var props = base.CreateProperties(type, memberSerialization);
            return props.OrderBy(p => p.PropertyName).ToList();
        }
    }



class OrderedExpandoPropertiesConverter : ExpandoObjectConverter
    {
        public override bool CanWrite
        {
            get { return true; }
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var expando = (IDictionary<string, object>)value;
            var orderedDictionary = expando.OrderBy(x => x.Key).ToDictionary(t => t.Key, t => t.Value);
            serializer.Serialize(writer, orderedDictionary);
        }
    }



var settings = new JsonSerializerSettings
        {
            ContractResolver = new OrderedPropertiesContractResolver(),
            Converters = { new OrderedExpandoPropertiesConverter() }
        };

var serializedString = JsonConvert.SerializeObject(obj, settings);
3
votes

As Charlie noted, you can somewhat control the ordering of the JSON properties by ordering the properties in the class itself. Unfortunately, this approach doesn't work for properties inherited from a base class. The base class properties will be ordered as they are laid out in code, but will appear before the base class properties.

And for anyone wondering why you might want to alphabetize JSON properties, it's a whole lot easier to work with raw JSON files, particularly for classes with lots of properties, if they are ordered.

3
votes

If you don't want to put a JsonProperty Order attribute on every class property, then its very simple to make your own ContractResolver...

The IContractResolver interface provides a way to customize how the JsonSerializer serializes and deserializes .NET objects to JSON without placing attributes on your classes.

Like this:

private class SortedPropertiesContractResolver : DefaultContractResolver
{
    // use a static instance for optimal performance
    static SortedPropertiesContractResolver instance;

    static SortedPropertiesContractResolver() { instance = new SortedPropertiesContractResolver(); }

    public static SortedPropertiesContractResolver Instance { get { return instance; } }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);
        if (properties != null)
            return properties.OrderBy(p => p.UnderlyingName).ToList();
        return properties;
    }
}

Implement:

var settings = new JsonSerializerSettings { ContractResolver = SortedPropertiesContractResolver.Instance };
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
2
votes

If you just want to pull a single attribute up to the front without thinking about the perhaps unintuitive number system, just use int.MinValue.

[JsonProperty(Order = int.MinValue)]
1
votes

Actually, since my Object was already a JObject, I used the following solution:

public class SortedJObject : JObject
{
    public SortedJObject(JObject other)
    {
        var pairs = new List<KeyValuePair<string, JToken>>();
        foreach (var pair in other)
        {
            pairs.Add(pair);
        }
        pairs.OrderBy(p => p.Key).ForEach(pair => this[pair.Key] = pair.Value);
    }
}

and then use it like this:

string serializedObj = JsonConvert.SerializeObject(new SortedJObject(dataObject));
0
votes

The following recursive method uses reflection to sort the internal token list on an existing JObject instance rather than creating a brand new sorted object graph. This code relies on internal Json.NET implementation details and should not be used in production.

void SortProperties(JToken token)
{
    var obj = token as JObject;
    if (obj != null)
    {
        var props = typeof (JObject)
            .GetField("_properties",
                      BindingFlags.NonPublic | BindingFlags.Instance)
            .GetValue(obj);
        var items = typeof (Collection<JToken>)
            .GetField("items", BindingFlags.NonPublic | BindingFlags.Instance)
            .GetValue(props);
        ArrayList.Adapter((IList) items)
            .Sort(new ComparisonComparer(
                (x, y) =>
                {
                    var xProp = x as JProperty;
                    var yProp = y as JProperty;
                    return xProp != null && yProp != null
                        ? string.Compare(xProp.Name, yProp.Name)
                        : 0;
                }));
    }
    foreach (var child in token.Children())
    {
        SortProperties(child);
    }
}
0
votes

If you control (i.e. write) the class, put the properties in alphabetical order and they will serialize in alphabetical order when JsonConvert.SerializeObject() is called.

0
votes

I want to serialize an comblex object and keep the order of the properties as they where defined in code. I can't just add [JsonProperty(Order = 1)] because the class itself is out of my scope.

This solution also takes into account that properties which are defined in a base class should have a higher priority.

This may not be bulletproof, since nowhere is defined that the MetaDataAttribute ensures the correct order, but it seems to work. For my use case this is ok. since I only want to maintain human readability for an auto generated config file.

public class PersonWithAge : Person
{
    public int Age { get; set; }
}

public class Person
{
    public string Name { get; set; }
}

public string GetJson()
{
    var thequeen = new PersonWithAge { Name = "Elisabeth", Age = Int32.MaxValue };

    var settings = new JsonSerializerSettings()
    {
        ContractResolver = new MetadataTokenContractResolver(),
    };

    return JsonConvert.SerializeObject(
        thequeen, Newtonsoft.Json.Formatting.Indented, settings
    );

}

public class MetadataTokenContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(
        Type type, MemberSerialization memberSerialization)
    {
        var props = type
           .GetProperties(BindingFlags.Instance
               | BindingFlags.Public
               | BindingFlags.NonPublic
           ).ToDictionary(k => k.Name, v =>
           {
               // first value: declaring type
               var classIndex = 0;
               var t = type;
               while (t != v.DeclaringType)
               {
                   classIndex++;
                   t = type.BaseType;
               }
               return Tuple.Create(classIndex, v.MetadataToken);
           });

        return base.CreateProperties(type, memberSerialization)
            .OrderByDescending(p => props[p.PropertyName].Item1)
            .ThenBy(p => props[p.PropertyName].Item1)
            .ToList();
    }
}

-1
votes

If you want to globally configure your API with ordered fields, please combine Mattias Nordberg answer:

public class OrderedContractResolver : DefaultContractResolver
{
    protected override System.Collections.Generic.IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
    {
        return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
    }
}

with my answer here:

How to force ASP.NET Web API to always return JSON?

-5
votes

UPDATE

I just saw the downvotes. Please see the answer from 'Steve' below for how to do this.

ORIGINAL

I followed the JsonConvert.SerializeObject(key) method call via reflection (where key was an IList) and found that JsonSerializerInternalWriter.SerializeList gets called. It takes a list and loops through via

for (int i = 0; i < values.Count; i++) { ...

where values is the IList parameter brought in.

Short answer is...No, there's no built in way to set the order the fields are listed in the JSON string.

-6
votes

There's no order of fields in the JSON format so defining an order doesn't make sense.

{ id: 1, name: 'John' } is equivalent to { name: 'John', id: 1 } (both represent a strictly equivalent object instance)