JsonConvert.SerializeObject 更改 JSON 中字段的排序顺序

JsonConvert.SerializeObject changes the sort order of fields in JSON

JsonConvert.SerializeObject 如果您在子线程中对正在序列化的对象调用 .GetProperty 方法,则会更改 JSON 中字段的排序顺序。

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

执行此代码后,控制台将显示以下内容:

Version字段在JSON的开头,而不是

的结尾

如果我们删除

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

如果我用属性 [JsonProperty (Order = 1)] 装饰我的对象,那么排序将与我在属性中指示的不一样

我该如何解决?在不使用 attr [JsonProperty (Order = 1)]

的情况下进行修复

已更新: 我们使用 JSON 字符串来生成数字签名,如果字段的顺序发生变化,数字签名将无效,所以字段的顺序对我来说很重要

原来 JsonConvert.SerializeObject 不能保证字段的默认顺序。要指定显式排序,您可以使用 DefaultContractResolver

感谢 Andy 的想法!

自定义的实现DefaultContractResolver

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

用法示例:

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

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

因为默认 JsonSerializer 使用 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)

在我看来,您不应该关心 Json 中属性的顺序。如果 json 消费者真的需要这个合同,我认为你应该审查你的设计。

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)