172

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.

Kevin Montrose
  • 22,191
  • 9
  • 88
  • 137
  • Just out of curiosity, why would you care? – Maxim Gershkovich Jul 03 '12 at 10:41
  • 8
    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 Nov 14 '12 at 14:53
  • 4
    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 Jun 30 '15 at 12:58
  • In Chrome and IE the order of properties in objects is important to remain on the fast path with hidden classes/Object Type Specialization: http://blogs.msdn.com/b/ie/archive/2014/10/09/announcing-key-advances-to-javascript-performance-in-windows-10-technical-preview.aspx – Ben Adams Jul 05 '15 at 07:30
  • 6
    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 Feb 13 '16 at 14:27
  • 5
    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 Mar 06 '18 at 14:58
  • One reason you might want to fix the order = to check the results of an API call against a local version. I am creating SHA256 hashes of the results of local vs remote calls (so easily establishing if all the data is 100% identical). – MemeDeveloper Mar 31 '19 at 12:55
  • 2
    Kevin, can you update the accepted answer on this question? – Millie Smith Jul 09 '19 at 22:13
  • Another reason to preserve ordering is working with git and other version management. It's easier to see actual changes and reduce the amount of conflicts. – Luc Bloom Jul 08 '21 at 05:44
  • Related for .Net Core 3.1+ https://stackoverflow.com/q/59134564/125981 – Mark Schultheiss May 03 '23 at 20:51

16 Answers16

288

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

Steve
  • 25,806
  • 2
  • 33
  • 43
  • 111
    Using the `Order` property of the `JsonPropertyAttribute` can be used to control the order in which fields are serialized/deserialized. However, 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. – Kevin Babcock Feb 25 '14 at 11:23
  • 2
    It works for serialization, but the order is not being considered on deserialization. According to the documentation, the order attribute is used for both serialization and deserialization. Is there a workaround? – cangosta Dec 21 '15 at 18:46
  • 1
    Is there a similar property for the `JavaScriptSerializer`. – Shimmy Weitzhandler Dec 31 '15 at 05:55
  • 5
    @cangosta The order for deserialization should not matter .. except in some very "odd" expectation cases. – user2864740 Dec 05 '16 at 06:08
  • 1
    Read the similar github issues discussion around the desire for Order to be respected in deserialisation: https://github.com/JamesNK/Newtonsoft.Json/issues/758 Basically no chance of this one. – Tyeth Apr 09 '19 at 14:40
145

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);
Mattias Nordberg
  • 1,748
  • 1
  • 11
  • 9
  • 13
    This is quite helpful (+1) but one caveat: it appears serialization of dictionaries doesn't use this CreateProperties customization. They serialize fine but don't end up sorted. I assume there's a different way to customize serialization of dictionaries, but I haven't found it. – solublefish Mar 06 '15 at 19:46
  • 1
    This is a great solution. Worked perfectly for me especially when putting 2 JSON objects side-by-side and having the properties line up. – Vince Apr 13 '17 at 19:29
23

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;
    }
}
niaher
  • 9,460
  • 7
  • 67
  • 86
  • 3
    This was the required fix for us when using dicts. – noocyte Jun 03 '15 at 08:39
  • This adds overhead of additional deserialization and serialization. I've added a solution which'll work for normal classes, dictionaries and ExpandoObject (dynamic object) as well – Jay Shah Mar 13 '19 at 18:00
  • 1
    Useful overall but missing handling of arrays (sort object keys inside objects inside arrays). Relatively easy to add. – evilkos May 26 '21 at 08:42
19

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;
        }
    }
}
Tuan-Tu Tran
  • 199
  • 1
  • 2
6

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);
Jay Shah
  • 3,553
  • 1
  • 27
  • 26
  • Wasn't this the default ordering behavior during serialization? – mr5 Sep 26 '19 at 02:48
  • 6
    To save someone else a few wasted minutes, note that this answer doesn't work for dictionaries despite the claim. `CreateProperties` is not invoked during serialization of a dictionary. I explored the JSON.net repo for what machinery is actually chugging through the dictionary entries. It doesn't hook into any `override` or other customization for ordering. It just takes the entries as-is from the object's enumerator. It seems I have to construct a `SortedDictionary` or `SortedList` to force JSON.net to do this. Feature suggestion filed: https://github.com/JamesNK/Newtonsoft.Json/issues/2270 – William Feb 03 '20 at 20:05
5

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)]
twinlakes
  • 9,438
  • 6
  • 31
  • 42
3

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.

Jack Bond
  • 448
  • 3
  • 8
3

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);
CrazyTim
  • 6,695
  • 6
  • 34
  • 55
2

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));
Danny R
  • 13,734
  • 1
  • 15
  • 12
2

This worked for me.

using System.Text.Json.Serialization;


[JsonPropertyOrder(-1)]
public string Id { get; set; }

I assume the default order of all properties is defaulted to 0 so using negative order puts them in front.

You could always put an order attribute on all of your properties with a positive number but that is a lot of work to do and keep up. In my case I just wanted the "id" field to show first and then everything else is sorted alphabetically. This just makes it easier to find the "id" in my case.

Have a great day!

Thomas
  • 3,532
  • 3
  • 20
  • 22
  • FWIW and for clarity this is .Net Core 3.1 and later or via a Nuget package ref: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-7-0 and the related https://stackoverflow.com/q/59134564/125981 – Mark Schultheiss May 03 '23 at 20:49
1

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);
    }
}
Nathan Baulch
  • 20,233
  • 5
  • 52
  • 56
0

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.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
Charlie
  • 11
  • This does not feel like a very sustainable solution but good to know. I can drive from Cleveland to Cincinnati to get a cab ride to a Cleveland suburb also if I am willing to go through the challenges entailed. :) – Mark Schultheiss May 03 '23 at 20:46
0

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();
    }
}

Jürgen Steinblock
  • 30,746
  • 24
  • 119
  • 189
-1

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

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.

DougJones
  • 774
  • 1
  • 9
  • 26
-7

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)

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • This only matters for the "on-the-wire" transmission, not for its consumption on the other side. Once deserialized I don't expect the order to persist, basically. – Kevin Montrose Jul 25 '10 at 21:03
  • What order are you talking about? JSON is a format allowing the serialization/deserialization of objects. There's no notion *order* in object fields in OOP. – Darin Dimitrov Jul 25 '10 at 21:04
  • 12
    @Darin - but there is an order in the serialization. "{ id: 1, name: 'John' }" and "{ name: 'John', id: 1 }" are different as *strings*, which is what I care about here. Of course, the objects are equivalent when deserialized. – Kevin Montrose Jul 25 '10 at 21:09
  • But why do you care about the string representation? Aren't you manipulating object instances? – Darin Dimitrov Jul 25 '10 at 21:10
  • 1
    @Darin - no, not in this case. I'm serializing something and then passing it along as a string to a service that only deals in strings (not JSON aware), and it'd be convenient for a variety of reasons for one field to appear first in the string. – Kevin Montrose Jul 25 '10 at 21:27
  • What is this service? What language is it written into? Doesn't the language used to write this service support JSON? Is it an OOP language? Are you saying that the service is manipulating strings directly? – Darin Dimitrov Jul 25 '10 at 21:29
  • @Darin - yes, strings directly and outside of my control. – Kevin Montrose Jul 26 '10 at 06:26
  • 1
    its good for testing also, being able to just look at the strings rather than having to deserialize. – Steve Dec 26 '12 at 02:21
  • The order actually does matter. Once deserialized, the order specified in JSON affects the order of keys returned by Object.keys(), for example. It's not always wise to rely on this ordering since it can become arbitrary if delete is used, but in some cases (like my code which builds a jqGrid column model from the property names of the first object in an array), it's useful. – Brandon Paddock Nov 10 '14 at 23:46
  • 9
    A stable serialization order is handy for cache validation too. It's trivial to take a checksum of a string - not true of a full object graph. – solublefish Mar 06 '15 at 19:25
  • @BrandonPaddock A client relying on the ordering after deserialization is broken, broken, broken (perhaps the only exclusion is a JSON editor where round-trip ordering is desired; and this is a separate question). This JSON specification explicitly indicates that objects/maps are unordered. This doesn't preclude the usefulness of ordered serialization for other means such as stability (as per solublefish's comment) and human expectedness-readability. – user2864740 Dec 05 '16 at 06:09
  • 1
    Serialization order is also handy when doing unit tests so that you can easily say that expected vs actual response strings are equal even when the order of the json properties are different. – anon Apr 11 '17 at 15:55