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.
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.
Câu trả lời:
Cách được hỗ trợ là sử dụng JsonProperty
thuộ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 JsonProperty
một Order
giá 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.Serialization
ngà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
Order
tính của JsonPropertyAttribute
có 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.
JavaScriptSerializer
.
Bạn thực sự có thể kiểm soát trật tự bằng cách thực hiện IContractResolver
hoặc trọng các DefaultContractResolver
's CreateProperties
phươ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);
Trong trường hợp của tôi, câu trả lời của Mattias không hoạt động. Các CreateProperties
phương pháp chưa từng được gọi.
Sau khi gỡ lỗi Newtonsoft.Json
nộ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;
}
}
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;
}
}
}
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.
Đ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);
CreateProperties
khô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ỳ override
hoặ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 SortedDictionary
hoặc SortedList
buộ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
Nếu bạn không muốn đặt một JsonProperty
Order
thuộ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);
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 JObject
cá 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);
}
}
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));
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();
}
}
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:
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.
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)