Thứ tự các trường được tuần tự hóa bằng JSON.NET


137

Có cách nào để xác định thứ tự các trường trong một đối tượng JSON được tuần tự hóa bằng JSON.NET không?

Sẽ là đủ để xác định rằng một trường duy nhất luôn xuất hiện đầu tiên.


7
Tôi nghĩ rằng anh ấy có thể quan tâm đến việc hiển thị trường ID (hoặc tương tự) trước, và sau đó là tất cả các trường khác. điều này thân thiện với người dùng cuối hơn là tìm kiếm nó sau các trường bắt đầu bằng A..I
Michael Bahig

3
Các thuộc tính JSON được định nghĩa là không có thứ tự. Tôi nghĩ sẽ hoàn toàn ổn khi buộc một lệnh OUTPUT cụ thể trong quá trình tuần tự hóa (vì mục đích của việc đánh dấu JSON có lẽ), nhưng sẽ là một quyết định tồi khi tạo ra một PHỤ THUỘC theo bất kỳ thứ tự cụ thể nào về khử lưu huỳnh.
DaBlick

5
Một vài lý do hợp lệ: (1) giả mạo một thuộc tính "$ type" phải là thuộc tính đầu tiên trong JSON, (2) cố gắng tạo JSON nén càng nhiều càng tốt
Stephen Chung

4
Một lý do khác có thể là (3) một biểu diễn chính tắc sử dụng cú pháp JSON - cùng một đối tượng phải được đảm bảo để tạo ra cùng một chuỗi JSON. Một thứ tự xác định của các thuộc tính là điều kiện tiên quyết cần thiết cho việc này.
MarkusSchaber

2
Kevin, bạn có thể cập nhật câu trả lời được chấp nhận cho câu hỏi này không?
Millie Smith

Câu trả lời:


255

Cách được hỗ trợ là sử dụng JsonPropertythuộc tính trên các thuộc tính lớp mà bạn muốn đặt thứ tự cho. Đọc tài liệu đặt hàng JsonPropertyAttribution để biết thêm thông tin.

Vượt qua JsonPropertymột Ordergiá trị và serializer sẽ chăm sóc phần còn lại.

 [JsonProperty(Order = 1)]

Điều này rất giống với

 DataMember(Order = 1) 

của những System.Runtime.Serializationngày

Đây là một lưu ý quan trọng từ @ kevin-babcock

... Đặt thứ tự thành 1 sẽ chỉ hoạt động nếu bạn đặt một đơn hàng lớn hơn 1 trên tất cả các thuộc tính khác. Theo mặc định, bất kỳ thuộc tính nào không có cài đặt Đơn hàng sẽ được cấp -1. Vì vậy, bạn phải cung cấp tất cả các thuộc tính và thứ tự nối tiếp hoặc đặt mục đầu tiên của bạn thành -2


97
Sử dụng thuộc Ordertính của JsonPropertyAttributecó thể được sử dụng để kiểm soát thứ tự các trường được tuần tự hóa / giải tuần tự hóa. Tuy nhiên, đặt thứ tự thành 1 sẽ chỉ hoạt động nếu bạn đặt một đơn hàng lớn hơn 1 trên tất cả các thuộc tính khác. Theo mặc định, bất kỳ thuộc tính nào không có cài đặt Đơn hàng sẽ được cấp -1. Vì vậy, bạn phải cung cấp tất cả các thuộc tính và thứ tự nối tiếp hoặc đặt mục đầu tiên của bạn thành -2.
Kevin Babcock

1
Nó hoạt động để tuần tự hóa, nhưng thứ tự không được xem xét về khử lưu huỳnh. Theo tài liệu, thuộc tính thứ tự được sử dụng cho cả tuần tự hóa và giải tuần tự hóa. Có một cách giải quyết?
cangosta

Có một tài sản tương tự cho JavaScriptSerializer.
Shimmy Weitzhandler

3
@cangosta Thứ tự khử lưu huỳnh không nên quan trọng .. ngoại trừ trong một số trường hợp kỳ vọng rất "kỳ quặc".
dùng2864740

Đọc các vấn đề tương tự về github xung quanh mong muốn Đặt hàng được tôn trọng trong việc giải trừ: github.com/JamesNK/Newtonsoft.Json/issues/758 Về cơ bản không có cơ hội này.
Tyeth

125

Bạn thực sự có thể kiểm soát trật tự bằng cách thực hiện IContractResolverhoặc trọng các DefaultContractResolver's CreatePropertiesphương pháp.

Đây là một ví dụ về cách thực hiện đơn giản của tôi trong IContractResolverđó sắp xếp các thuộc tính theo thứ tự abc:

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

Sau đó, thiết lập các cài đặt và tuần tự hóa đối tượng và các trường JSON sẽ theo thứ tự bảng chữ cái:

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

var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);

11
Điều này khá hữu ích (+1) nhưng một điều lưu ý: nó xuất hiện tuần tự hóa các từ điển không sử dụng tùy chỉnh CreatProperies này. Họ tuần tự tốt nhưng cuối cùng không được sắp xếp. Tôi giả sử có một cách khác nhau để tùy chỉnh việc xê-ri hóa từ điển, nhưng tôi không tìm thấy nó.
cá hòa tan

Hoàn hảo. Có phải những gì tôi muốn. Cảm ơn.
Wade Hatler

Đây là một giải pháp tuyệt vời. Làm việc hoàn hảo với tôi đặc biệt là khi đặt 2 đối tượng JSON cạnh nhau và sắp xếp các thuộc tính.
Vince

16

Trong trường hợp của tôi, câu trả lời của Mattias không hoạt động. Các CreatePropertiesphương pháp chưa từng được gọi.

Sau khi gỡ lỗi Newtonsoft.Jsonnội bộ, tôi đã đưa ra một giải pháp khác.

public class JsonUtility
{
    public static string NormalizeJsonString(string json)
    {
        // Parse json string into JObject.
        var parsedObject = JObject.Parse(json);

        // Sort properties of JObject.
        var normalizedObject = SortPropertiesAlphabetically(parsedObject);

        // Serialize JObject .
        return JsonConvert.SerializeObject(normalizedObject);
    }

    private static JObject SortPropertiesAlphabetically(JObject original)
    {
        var result = new JObject();

        foreach (var property in original.Properties().ToList().OrderBy(p => p.Name))
        {
            var value = property.Value as JObject;

            if (value != null)
            {
                value = SortPropertiesAlphabetically(value);
                result.Add(property.Name, value);
            }
            else
            {
                result.Add(property.Name, property.Value);
            }
        }

        return result;
    }
}

2
Đây là sửa chữa cần thiết cho chúng tôi khi sử dụng dicts.
noocyte

Điều này bổ sung thêm chi phí khử tuần tự hóa và tuần tự hóa. Tôi đã thêm một giải pháp sẽ hoạt động cho các lớp thông thường, từ điển và ExpandoObject (đối tượng động)
Jay Shah

11

Trong trường hợp của tôi, giải pháp của niaher không hoạt động vì nó không xử lý các đối tượng trong mảng.

Dựa trên giải pháp của anh ấy, đây là những gì tôi nghĩ ra

public static class JsonUtility
{
    public static string NormalizeJsonString(string json)
    {
        JToken parsed = JToken.Parse(json);

        JToken normalized = NormalizeToken(parsed);

        return JsonConvert.SerializeObject(normalized);
    }

    private static JToken NormalizeToken(JToken token)
    {
        JObject o;
        JArray array;
        if ((o = token as JObject) != null)
        {
            List<JProperty> orderedProperties = new List<JProperty>(o.Properties());
            orderedProperties.Sort(delegate(JProperty x, JProperty y) { return x.Name.CompareTo(y.Name); });
            JObject normalized = new JObject();
            foreach (JProperty property in orderedProperties)
            {
                normalized.Add(property.Name, NormalizeToken(property.Value));
            }
            return normalized;
        }
        else if ((array = token as JArray) != null)
        {
            for (int i = 0; i < array.Count; i++)
            {
                array[i] = NormalizeToken(array[i]);
            }
            return array;
        }
        else
        {
            return token;
        }
    }
}

Điều này bổ sung thêm chi phí khử tuần tự hóa và tuần tự hóa.
Jay Shah

Giải pháp tuyệt vời. Cảm ơn bạn.
MaYaN

3

Như Charlie đã lưu ý, bạn có thể kiểm soát phần nào thứ tự các thuộc tính JSON bằng cách sắp xếp các thuộc tính trong chính lớp đó. Thật không may, cách tiếp cận này không hoạt động đối với các thuộc tính được kế thừa từ một lớp cơ sở. Các thuộc tính của lớp cơ sở sẽ được sắp xếp theo thứ tự chúng được đặt trong mã, nhưng sẽ xuất hiện trước các thuộc tính của lớp cơ sở.

Và đối với bất kỳ ai thắc mắc tại sao bạn có thể muốn sắp xếp thứ tự các thuộc tính JSON, thì việc làm việc với các tệp JSON thô sẽ dễ dàng hơn rất nhiều, đặc biệt là đối với các lớp có nhiều thuộc tính, nếu chúng được đặt hàng.


2

Điều này sẽ làm việc cho các lớp bình thường, từ điển và ExpandoObject (đối tượng động).

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



class OrderedExpandoPropertiesConverter : ExpandoObjectConverter
    {
        public override bool CanWrite
        {
            get { return true; }
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var expando = (IDictionary<string, object>)value;
            var orderedDictionary = expando.OrderBy(x => x.Key).ToDictionary(t => t.Key, t => t.Value);
            serializer.Serialize(writer, orderedDictionary);
        }
    }



var settings = new JsonSerializerSettings
        {
            ContractResolver = new OrderedPropertiesContractResolver(),
            Converters = { new OrderedExpandoPropertiesConverter() }
        };

var serializedString = JsonConvert.SerializeObject(obj, settings);

Đây không phải là hành vi đặt hàng mặc định trong quá trình tuần tự hóa sao?
mr5

1
Để tiết kiệm cho người khác một vài phút lãng phí, lưu ý rằng câu trả lời này không hoạt động cho từ điển mặc dù yêu cầu. CreatePropertieskhông được gọi trong quá trình tuần tự hóa từ điển. Tôi đã khám phá repo JSON.net cho những gì máy móc thực sự đang hoạt động thông qua các mục từ điển. Nó không móc vào bất kỳ overridehoặc tùy chỉnh khác để đặt hàng. Nó chỉ lấy các mục như là từ liệt kê của đối tượng. Có vẻ như tôi phải xây dựng một SortedDictionaryhoặc SortedListbuộc JSON.net phải làm điều này. Đề xuất tính năng đã nộp: github.com/JamesNK/Newtonsoft.Json/issues/2270
William

2

Nếu bạn không muốn đặt một JsonProperty Orderthuộc tính cho mọi thuộc tính của lớp, thì việc tạo ContractResolver của riêng bạn rất đơn giản ...

Giao diện IContractResolver cung cấp một cách để tùy chỉnh cách JsonSerializer tuần tự hóa và giải tuần tự hóa các đối tượng .NET thành JSON mà không đặt các thuộc tính trên các lớp của bạn.

Như thế này:

private class SortedPropertiesContractResolver : DefaultContractResolver
{
    // use a static instance for optimal performance
    static SortedPropertiesContractResolver instance;

    static SortedPropertiesContractResolver() { instance = new SortedPropertiesContractResolver(); }

    public static SortedPropertiesContractResolver Instance { get { return instance; } }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);
        if (properties != null)
            return properties.OrderBy(p => p.UnderlyingName).ToList();
        return properties;
    }
}

Triển khai thực hiện:

var settings = new JsonSerializerSettings { ContractResolver = SortedPropertiesContractResolver.Instance };
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);

0

Phương thức đệ quy sau sử dụng sự phản chiếu để sắp xếp danh sách mã thông báo nội bộ trên một JObjectcá thể hiện có thay vì tạo một biểu đồ đối tượng được sắp xếp hoàn toàn mới. Mã này dựa trên chi tiết triển khai Json.NET nội bộ và không nên được sử dụng trong sản xuất.

void SortProperties(JToken token)
{
    var obj = token as JObject;
    if (obj != null)
    {
        var props = typeof (JObject)
            .GetField("_properties",
                      BindingFlags.NonPublic | BindingFlags.Instance)
            .GetValue(obj);
        var items = typeof (Collection<JToken>)
            .GetField("items", BindingFlags.NonPublic | BindingFlags.Instance)
            .GetValue(props);
        ArrayList.Adapter((IList) items)
            .Sort(new ComparisonComparer(
                (x, y) =>
                {
                    var xProp = x as JProperty;
                    var yProp = y as JProperty;
                    return xProp != null && yProp != null
                        ? string.Compare(xProp.Name, yProp.Name)
                        : 0;
                }));
    }
    foreach (var child in token.Children())
    {
        SortProperties(child);
    }
}

0

Trên thực tế, vì Object của tôi đã là JObject, tôi đã sử dụng giải pháp sau:

public class SortedJObject : JObject
{
    public SortedJObject(JObject other)
    {
        var pairs = new List<KeyValuePair<string, JToken>>();
        foreach (var pair in other)
        {
            pairs.Add(pair);
        }
        pairs.OrderBy(p => p.Key).ForEach(pair => this[pair.Key] = pair.Value);
    }
}

và sau đó sử dụng nó như thế này:

string serializedObj = JsonConvert.SerializeObject(new SortedJObject(dataObject));

0

Nếu bạn điều khiển (tức là viết) lớp, hãy đặt các thuộc tính theo thứ tự bảng chữ cái và chúng sẽ tuần tự theo thứ tự bảng chữ cái khi JsonConvert.SerializeObject()được gọi.


0

Tôi muốn tuần tự hóa một đối tượng comblex và giữ thứ tự các thuộc tính như chúng được định nghĩa trong mã. Tôi không thể chỉ thêm[JsonProperty(Order = 1)] vì lớp học nằm ngoài phạm vi của tôi.

Giải pháp này cũng tính đến các thuộc tính được xác định trong lớp cơ sở nên có mức độ ưu tiên cao hơn.

Điều này có thể không chống đạn, vì không nơi nào được xác định là MetaDataAttributeđảm bảo đúng thứ tự, nhưng nó dường như hoạt động. Đối với trường hợp sử dụng của tôi điều này là ok. vì tôi chỉ muốn duy trì khả năng đọc của con người đối với tệp cấu hình được tạo tự động.

public class PersonWithAge : Person
{
    public int Age { get; set; }
}

public class Person
{
    public string Name { get; set; }
}

public string GetJson()
{
    var thequeen = new PersonWithAge { Name = "Elisabeth", Age = Int32.MaxValue };

    var settings = new JsonSerializerSettings()
    {
        ContractResolver = new MetadataTokenContractResolver(),
    };

    return JsonConvert.SerializeObject(
        thequeen, Newtonsoft.Json.Formatting.Indented, settings
    );

}

public class MetadataTokenContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(
        Type type, MemberSerialization memberSerialization)
    {
        var props = type
           .GetProperties(BindingFlags.Instance
               | BindingFlags.Public
               | BindingFlags.NonPublic
           ).ToDictionary(k => k.Name, v =>
           {
               // first value: declaring type
               var classIndex = 0;
               var t = type;
               while (t != v.DeclaringType)
               {
                   classIndex++;
                   t = type.BaseType;
               }
               return Tuple.Create(classIndex, v.MetadataToken);
           });

        return base.CreateProperties(type, memberSerialization)
            .OrderByDescending(p => props[p.PropertyName].Item1)
            .ThenBy(p => props[p.PropertyName].Item1)
            .ToList();
    }
}


-1

Nếu bạn muốn định cấu hình toàn cầu API của mình với các trường được đặt hàng, vui lòng kết hợp câu trả lời của Mattias Nordberg:

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

với câu trả lời của tôi ở đây:

Làm cách nào để buộc API Web ASP.NET luôn trả về JSON?


-5

CẬP NHẬT

Tôi chỉ thấy các downvote. Vui lòng xem câu trả lời từ 'Steve' bên dưới để biết cách thực hiện việc này.

NGUYÊN

Tôi đã thực hiện JsonConvert.SerializeObject(key)cuộc gọi phương thức thông qua sự phản chiếu (trong đó khóa là IList) và thấy rằng JsonSerializerI INTERNalWriter.SerializeList được gọi. Nó có một danh sách và các vòng lặp thông qua

for (int i = 0; i < values.Count; i++) { ...

trong đó các giá trị là tham số IList được đưa vào.

Câu trả lời ngắn gọn là ... Không, không có cách nào được thiết lập để đặt thứ tự các trường được liệt kê trong chuỗi JSON.


18
Câu trả lời ngắn, nhưng có thể đã lỗi thời. Kiểm tra câu trả lời của Steve (được hỗ trợ bởi James Newton-king)
Brad Bruce

-6

Không có thứ tự các trường trong định dạng JSON nên việc xác định thứ tự không có ý nghĩa.

{ id: 1, name: 'John' }tương đương với { name: 'John', id: 1 }(cả hai đều đại diện cho một thể hiện đối tượng tương đương nghiêm ngặt)


12
@Darin - nhưng có một thứ tự trong tuần tự hóa. "{id: 1, name: 'John'}" và "{name: 'John', id: 1}" khác nhau như các chuỗi , đó là điều tôi quan tâm ở đây. Tất nhiên, các đối tượng là tương đương khi khử lưu huỳnh.
Kevin Montrose

1
@Darin - không, không phải trong trường hợp này. Tôi đang tuần tự hóa một cái gì đó và sau đó chuyển nó thành một chuỗi cho một dịch vụ chỉ xử lý các chuỗi (không nhận biết JSON) và sẽ thuận tiện vì nhiều lý do cho một trường xuất hiện đầu tiên trong chuỗi.
Kevin Montrose

1
nó cũng tốt cho việc kiểm tra, có thể chỉ nhìn vào các chuỗi thay vì phải khử lưu huỳnh.
Steve

9
Một thứ tự tuần tự hóa ổn định cũng có ích cho việc xác thực bộ đệm. Việc kiểm tra một chuỗi là không quan trọng - không đúng với biểu đồ đối tượng đầy đủ.
cá hòa tan

1
Thứ tự tuần tự hóa cũng tiện dụng khi thực hiện các bài kiểm tra đơn vị để bạn có thể dễ dàng nói rằng các chuỗi phản hồi dự kiến ​​so với thực tế là bằng nhau ngay cả khi thứ tự của các thuộc tính json khác nhau.
anon
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.