Home > Software design >  JsonConvert.SerializeObject changes the sort order of fields in JSON
JsonConvert.SerializeObject changes the sort order of fields in JSON

Time:11-25

JsonConvert.SerializeObject changes the sorting order of fields in JSON if you call the .GetProperty method on the object being serialized in the child thread.

class Program
{

    static void Main(string[] args)
    {
        var tasks = new List<Task>();
        for (int i = 0; i < 10; i  )
        {
            var task = Task.Factory.StartNew(() =>
            {
                var token = CreateRandomToken();

                _ = typeof(TestObject).GetProperty("Version");

                var str = JsonConvert.SerializeObject(token);

                Console.WriteLine(str);
            });

            tasks.Add(task);
        }

        Task.WaitAll(tasks.ToArray());

        Console.ReadLine();
    }


    private static TestObject CreateRandomToken()
        => new TestObject { TokenHash = "123456789", Name = "Name", Version = "123" };

}

public class TestObject
{
    public string TokenHash { get; set; }

    public string Name { get; set; }

    public string Version { get; set; }
}

As a result of executing this code, the following will be displayed on the console: enter image description here

The Version field is at the beginning of the JSON, not at the end

If we remove

_ = typeof(TestObject).GetProperty("Version"); 
  • then the sorting of the fields will not change or if you call code in the main thread, then the sorting will not change either

enter image description here

if I decorate my object with the attributes [JsonProperty (Order = 1)] then the sorting will not be the same as I indicated in the attributes

How can I fix it? fix without using the attr [JsonProperty (Order = 1)]

Updated: We use a JSON string to generate a digital signature if the order of the fields changes the digital signature will not be valid, so the order of the fields is important for me

CodePudding user response:

Because the default JsonSerializer get properties using System.Type.GetProperties().

The GetProperties method does not return properties in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which properties are returned, because that order varies. (Source Type.GetProperties Method)

In my opinion, you shouldn't care about the order of properties in Json. If the json consumer really need this contract, I think you should review your design.

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. (Source RFC 7159)

CodePudding user response:

It turns out that JsonConvert.SerializeObject doesn't guarantee the default order of fields. To specify an explicit sorting, you can use the DefaultContractResolver

Thanks Andy for the idea!

Implementation of custom DefaultContractResolver:

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

Usage example:

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

 var str = JsonConvert.SerializeObject(token, jsonSerializerSettings);
  • Related