Apakah ada cara untuk menentukan urutan bidang dalam objek JSON berseri menggunakan JSON.NET ?
Itu akan cukup untuk menentukan bahwa satu bidang selalu muncul terlebih dahulu.
Apakah ada cara untuk menentukan urutan bidang dalam objek JSON berseri menggunakan JSON.NET ?
Itu akan cukup untuk menentukan bahwa satu bidang selalu muncul terlebih dahulu.
Jawaban:
Cara yang didukung adalah dengan menggunakan JsonProperty
atribut pada properti kelas yang ingin Anda atur urutannya. Baca dokumentasi pesanan JsonPropertyAttribute untuk informasi lebih lanjut.
Lulus JsonProperty
sebuah Order
nilai dan serializer akan mengurus sisanya.
[JsonProperty(Order = 1)]
Ini sangat mirip dengan
DataMember(Order = 1)
System.Runtime.Serialization
hari - hari.
Ini adalah catatan penting dari @ kevin-babcock
... mengatur pesanan ke 1 hanya akan berfungsi jika Anda menetapkan pesanan lebih besar dari 1 pada semua properti lainnya. Secara default, properti apa pun tanpa pengaturan Pesanan akan diberi urutan -1. Jadi, Anda harus memberikan semua properti dan pesanan berseri, atau mengatur item pertama Anda ke -2
Order
properti dari JsonPropertyAttribute
dapat digunakan untuk mengontrol urutan bidang yang serial / deserialized. Namun, mengatur pesanan ke 1 hanya akan berfungsi jika Anda menetapkan pesanan lebih besar dari 1 pada semua properti lainnya. Secara default, properti apa pun tanpa pengaturan Pesanan akan diberi urutan -1. Jadi, Anda harus memberikan semua properti dan pesanan berseri, atau mengatur item pertama Anda ke -2.
JavaScriptSerializer
.
Anda benar-benar dapat mengontrol urutan dengan menerapkan IContractResolver
atau override DefaultContractResolver
's CreateProperties
metode.
Berikut adalah contoh implementasi sederhana saya IContractResolver
yang memesan properti secara alfabet:
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();
}
}
Dan kemudian mengatur pengaturan dan membuat serial objek, dan bidang JSON akan berada dalam urutan abjad:
var settings = new JsonSerializerSettings()
{
ContractResolver = new OrderedContractResolver()
};
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
Dalam kasus saya, jawaban Mattias tidak berhasil. The CreateProperties
Metode tidak pernah disebut.
Setelah debugging Newtonsoft.Json
internal, saya datang dengan solusi lain.
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;
}
}
Dalam kasus saya, solusi niaher tidak berfungsi karena tidak menangani objek dalam array.
Berdasarkan solusinya, inilah yang saya hasilkan
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;
}
}
}
Seperti yang dicatat Charlie, Anda dapat mengontrol urutan properti JSON dengan memesan properti di kelas itu sendiri. Sayangnya, pendekatan ini tidak berfungsi untuk properti yang diwarisi dari kelas dasar. Properti kelas dasar akan dipesan karena mereka diletakkan dalam kode, tetapi akan muncul sebelum properti kelas dasar.
Dan bagi siapa pun yang bertanya-tanya mengapa Anda mungkin ingin mengabjadkan properti JSON, jauh lebih mudah untuk bekerja dengan file JSON mentah, terutama untuk kelas dengan banyak properti, jika dipesan.
Ini akan berfungsi untuk kelas normal, kamus dan ExpandoObject (objek dinamis) juga.
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
tidak dipanggil selama serialisasi kamus. Saya menjelajahi repo JSON.net untuk mesin apa yang sebenarnya menembus entri kamus. Itu tidak terhubung ke override
kustomisasi apa pun atau lainnya untuk memesan. Itu hanya mengambil entri apa adanya dari pencacah objek. Sepertinya saya harus membuat SortedDictionary
atau SortedList
memaksa JSON.net untuk melakukan ini. Saran fitur diajukan: github.com/JamesNK/Newtonsoft.Json/issues/2270
Jika Anda tidak ingin menempatkan JsonProperty
Order
atribut pada setiap properti kelas, maka sangat mudah untuk membuat ContractResolver Anda sendiri ...
Antarmuka IContractResolver menyediakan cara untuk menyesuaikan bagaimana JsonSerializer membuat serial dan deserializes .NET objek ke JSON tanpa menempatkan atribut pada kelas Anda.
Seperti ini:
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;
}
}
Melaksanakan:
var settings = new JsonSerializerSettings { ContractResolver = SortedPropertiesContractResolver.Instance };
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
Metode rekursif berikut menggunakan refleksi untuk mengurutkan daftar token internal pada JObject
instance yang ada daripada membuat grafik objek yang diurutkan baru. Kode ini bergantung pada detail implementasi Json.NET internal dan tidak boleh digunakan dalam produksi.
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);
}
}
Sebenarnya, karena Object saya sudah menjadi JObject, saya menggunakan solusi berikut:
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);
}
}
dan kemudian gunakan seperti ini:
string serializedObj = JsonConvert.SerializeObject(new SortedJObject(dataObject));
Saya ingin membuat serial objek comblex dan menjaga urutan properti seperti yang didefinisikan dalam kode. Saya tidak bisa menambahkan [JsonProperty(Order = 1)]
karena kelas itu sendiri berada di luar jangkauan saya.
Solusi ini juga memperhitungkan bahwa properti yang didefinisikan dalam kelas dasar harus memiliki prioritas yang lebih tinggi.
Ini mungkin bukan antipeluru, karena tidak ada tempat yang MetaDataAttribute
memastikan bahwa urutan yang benar, tetapi tampaknya berfungsi. Untuk kasus penggunaan saya ini ok. karena saya hanya ingin mempertahankan keterbacaan manusia untuk file konfigurasi yang dihasilkan secara otomatis.
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();
}
}
Jika Anda ingin mengonfigurasi API Anda secara global dengan bidang yang dipesan, silakan gabungkan jawaban 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();
}
}
dengan jawaban saya di sini:
Bagaimana cara memaksa ASP.NET Web API untuk selalu mengembalikan JSON?
MEMPERBARUI
Saya baru saja melihat downvotes. Silakan lihat jawaban dari 'Steve' di bawah untuk cara melakukan ini.
ASLI
Saya mengikuti JsonConvert.SerializeObject(key)
pemanggilan metode melalui refleksi (di mana kuncinya adalah IList) dan menemukan bahwa JsonSerializerInternalWriter.SerializeList dipanggil. Dibutuhkan daftar dan loop melalui
for (int i = 0; i < values.Count; i++) { ...
di mana nilai adalah parameter IList yang dibawa.
Jawaban singkatnya adalah ... Tidak, tidak ada cara untuk mengatur urutan kolom yang tercantum dalam string JSON.
Tidak ada urutan bidang dalam format JSON sehingga mendefinisikan pesanan tidak masuk akal.
{ id: 1, name: 'John' }
setara dengan { name: 'John', id: 1 }
(keduanya mewakili instance objek yang sangat setara)