【问题标题】:JsonConvert.SerializeObject changes the sort order of fields in JSONJsonConvert.SerializeObject 更改 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 中找到的顺序。然后,您可以手动操作对象层次结构,而不是序列化/反序列化。

标签: c# json json.net


【解决方案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);
    

    【讨论】:

      猜你喜欢
      • 2019-07-04
      • 1970-01-01
      • 1970-01-01
      • 2019-05-24
      • 1970-01-01
      • 1970-01-01
      • 2015-06-12
      • 1970-01-01
      • 2013-12-09
      相关资源
      最近更新 更多