JSON.NET kullanarak serileştirilmiş bir JSON nesnesindeki alanların sırasını belirtmek için bir yolu var mı ?
Her zaman önce tek bir alanın görüneceğini belirtmek yeterli olacaktır.
JSON.NET kullanarak serileştirilmiş bir JSON nesnesindeki alanların sırasını belirtmek için bir yolu var mı ?
Her zaman önce tek bir alanın görüneceğini belirtmek yeterli olacaktır.
Yanıtlar:
Desteklenen yol, JsonProperty
niteliğini, sırasını ayarlamak istediğiniz sınıf özelliklerinde kullanmaktır . Daha fazla bilgi için JsonPropertyAttribute sipariş belgelerini okuyun .
Geçiş JsonProperty
bir Order
değeri ve seri hale gerisini halleder.
[JsonProperty(Order = 1)]
Bu çok benzer
DataMember(Order = 1)
arasında System.Runtime.Serialization
günlerde.
İşte @ kevin-babcock'dan önemli bir not
... siparişin 1 olarak ayarlanması yalnızca diğer tüm özellikler için 1'den büyük bir sipariş ayarlarsanız çalışır. Varsayılan olarak, Sipariş ayarı olmayan herhangi bir özelliğe -1 sırası verilir. Bu nedenle, tüm serileştirilmiş özellikleri ve siparişi vermeli veya ilk öğenizi -2 olarak ayarlamalısınız
Order
özelliğini kullanmak, JsonPropertyAttribute
alanların serileştirildiği / serileştirildiği sırayı kontrol etmek için kullanılabilir. Ancak, siparişin 1 olarak ayarlanması yalnızca diğer tüm özellikler için 1'den büyük bir sipariş ayarladıysanız çalışır. Varsayılan olarak, Sipariş ayarı olmayan herhangi bir özelliğe -1 sırası verilir. Bu nedenle, tüm serileştirilmiş özellikleri ve siparişi vermeli veya ilk öğenizi -2 olarak ayarlamalısınız.
JavaScriptSerializer
?
'S yöntemini uygulayarak IContractResolver
veya geçersiz kılarak siparişi kontrol edebilirsiniz .DefaultContractResolver
CreateProperties
İşte IContractResolver
özelliklerinin alfabetik olarak sıralandığı basit uygulamamın bir örneği :
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();
}
}
Ardından ayarları yapın ve nesneyi serileştirin; JSON alanları alfabetik sırada olacaktır:
var settings = new JsonSerializerSettings()
{
ContractResolver = new OrderedContractResolver()
};
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
Benim durumumda Mattias'ın cevabı işe yaramadı. CreateProperties
Yöntem hiç çağrılmadı.
Newtonsoft.Json
Dahili hata ayıklamadan sonra başka bir çözüm buldum.
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;
}
}
Benim durumumda, niaher'ın çözümü işe yaramadı çünkü dizilerdeki nesneleri işlemedi.
Onun çözümüne dayanarak bulduğum şey bu
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;
}
}
}
Charlie'nin belirttiği gibi, sınıfın kendisindeki özellikleri sipariş ederek JSON özelliklerinin sırasını biraz kontrol edebilirsiniz. Ne yazık ki, bu yaklaşım bir temel sınıftan miras alınan mülkler için geçerli değildir. Temel sınıf özellikleri, kodda belirtildiği gibi sıralanır, ancak temel sınıf özelliklerinden önce görünür.
Ve neden JSON özelliklerini alfabetikleştirmek isteyebileceğinizi merak eden herkes için, sipariş edilirlerse, özellikle çok sayıda özelliğe sahip sınıflar için ham JSON dosyalarıyla çalışmak çok daha kolaydır.
Bu normal sınıflar, sözlükler ve ExpandoObject (dinamik nesne) için de işe yarayacaktır.
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
sözlüğün serileştirilmesi sırasında çağrılmaz. JSON.net repo'yu, makine girişleri üzerinden gerçekte ne çektiğini keşfettim. override
Sipariş için herhangi bir veya başka bir özelleştirmeye bağlanmaz. Yalnızca girişleri nesnenin numaralandırıcısından olduğu gibi alır. Görünüşe göre bunu yapmak için JSON.net'i zorlamak SortedDictionary
ya SortedList
da zorlamak zorundayım. Özellik öneri yayınlandı: github.com/JamesNK/Newtonsoft.Json/issues/2270
JsonProperty
Order
Her sınıf mülke bir özellik koymak istemiyorsanız , o zaman kendi ContractResolver'ınızı yapmak çok basit ...
IContractResolver arabirimi, JsonSerializer'ın sınıflarınıza öznitelik yerleştirmeden .NET nesnelerini JSON'a nasıl serileştirdiğini ve serileştirdiğini özelleştirmek için bir yol sağlar.
Bunun gibi:
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;
}
}
Uygulanışı:
var settings = new JsonSerializerSettings { ContractResolver = SortedPropertiesContractResolver.Instance };
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
Aşağıdaki özyinelemeli yöntem, JObject
yepyeni bir sıralanmış nesne grafiği oluşturmak yerine , dahili belirteç listesini var olan bir örnekte sıralamak için yansıma kullanır . Bu kod, dahili Json.NET uygulama ayrıntılarına dayanır ve üretimde kullanılmamalıdır.
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);
}
}
Aslında, Nesnem zaten bir JObject olduğundan, aşağıdaki çözümü kullandım:
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);
}
}
ve sonra şu şekilde kullanın:
string serializedObj = JsonConvert.SerializeObject(new SortedJObject(dataObject));
Bir comblex nesnesini serileştirmek ve kodda tanımlandığı gibi özelliklerin sırasını tutmak istiyorum. Ekleyemiyorum [JsonProperty(Order = 1)]
çünkü sınıfın kendisi kapsamımın dışında.
Bu çözüm ayrıca, bir temel sınıfta tanımlanan özelliklerin daha yüksek önceliğe sahip olması gerektiğini de dikkate alır.
Bu, kurşun geçirmez olmayabilir, çünkü hiçbir yerde MetaDataAttribute
doğru siparişin temin edildiği tanımlanmamıştır , ancak işe yarıyor gibi görünüyor. Benim kullanım durumum için bu tamam. çünkü sadece otomatik olarak oluşturulmuş bir yapılandırma dosyası için okunabilirliği korumak istiyorum.
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();
}
}
API'nızı sipariş edilen alanlarla global olarak yapılandırmak istiyorsanız, lütfen Mattias Nordberg yanıtını birleştirin:
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();
}
}
cevabım burada:
GÜNCELLEME
Az önce yokuşları gördüm. Bunun nasıl yapılacağı için lütfen aşağıdaki 'Steve'in cevabına bakınız.
ORİJİNAL
JsonConvert.SerializeObject(key)
Yöntem çağrısını yansıma ile izledim (burada anahtar bir IList idi) ve JsonSerializerInternalWriter.SerializeList'in çağrıldığını buldum. Bir liste alır ve üzerinden dolaşır
for (int i = 0; i < values.Count; i++) { ...
burada değerler getirilen IList parametresidir.
Kısa cevap ... Hayır, alanların JSON dizesinde listelenen sıralamayı ayarlamak için yerleşik bir yöntem yoktur.
JSON biçimindeki alanların sırası yoktur, bu nedenle bir sipariş tanımlamak mantıklı değildir.
{ id: 1, name: 'John' }
eşittir { name: 'John', id: 1 }
(her ikisi de kesinlikle eşdeğer bir nesne örneğini temsil eder)