簡體   English   中英

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

[英]JsonConvert.SerializeObject changes the sort order of fields in JSON

如果在子線程中對正在序列化的 object 調用.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)]裝飾我的 object,那么排序將與我在屬性中指示的不同

我該如何解決? 不使用 attr [JsonProperty (Order = 1)]進行修復

更新:我們使用 JSON 字符串來生成數字簽名,如果字段的順序發生更改,數字簽名將無效,因此字段的順序對我來說很重要

因為默認JsonSerializer使用System.Type.GetProperties()獲取屬性。

GetProperties 方法不按特定順序返回屬性,例如字母順序或聲明順序。 您的代碼不得依賴於返回屬性的順序,因為該順序會有所不同。 (源類型.GetProperties 方法

在我看來,你不應該關心 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. (來源RFC 7159

事實證明JsonConvert.SerializeObject不保證字段的默認順序。 要指定顯式排序,您可以使用DefaultContractResolver

謝謝安迪的主意!

自定義DefaultContractResolver的實現:

 public class OrderedContractResolver : DefaultContractResolver
    {

        private static ConcurrentDictionary<Type, IList<string>> _properties = new ConcurrentDictionary<Type, IList<string>>();

        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {

            if (!_properties.TryGetValue(type, out var properties))
            {
                properties = type.GetProperties().Select(p => p.Name).ToList();
                _properties.TryAdd(type, properties);
            }
            
            var jsonProperties = base.CreateProperties(type, memberSerialization).ToList();

            var sorted = new List<JsonProperty>();
            foreach (var property in properties)
            {
                var jsonProp = jsonProperties.FirstOrDefault(p => p.PropertyName == property);
                if (jsonProp != null)
                {
                    sorted.Add(jsonProp);
                }
            }

            return sorted;
        }
    }

使用示例:

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM