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

Posted

技术标签:

【中文标题】JsonConvert.SerializeObject 更改 JSON 中字段的排序顺序【英文标题】:JsonConvert.SerializeObject changes the sort order of fields in JSON 【发布时间】:2022-01-03 10:25:54 【问题描述】:

如果您在子线程中对正在序列化的对象调用.GetProperty 方法,JsonConvert.SerializeObject 会更改 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 字符串生成数字签名,如果字段顺序发生更改,数字签名将无效,因此字段顺序对我来说很重要

【问题讨论】:

使用属性是你修复它的方法。为什么会出现这个问题? 我不想将属性挂在所有类上,因为这些类有很多,而且它们都可能用于序列化为 JSON 我会考虑实施DefaultContractResolver 或继续使用JsonProperty。除非您指定,否则属性不会保持其顺序。他们为什么要默认保持订单? 我的意思是为什么订单有问题? 我相信对根 JSON 对象使用 JObject.Parse 或类似内容会保持在 JSON 中找到的顺序。然后,您可以手动操作对象层次结构,而不是序列化/反序列化。 【参考方案1】:

因为默认JsonSerializer使用System.Type.GetProperties()获取属性。

GetProperties 方法不按特定顺序返回属性,例如字母顺序或声明顺序。您的代码不得依赖于返回属性的顺序,因为该顺序会有所不同。 (来源Type.GetProperties Method)

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

对象是零个或多个名称/值的无序集合 对,其中名称是字符串,值是字符串、数字、 布尔值、空值、对象或数组。 (来源RFC 7159)

【讨论】:

【参考方案2】:

原来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);

【讨论】:

以上是关于JsonConvert.SerializeObject 更改 JSON 中字段的排序顺序的主要内容,如果未能解决你的问题,请参考以下文章