Gibt es eine Möglichkeit, die Reihenfolge der Felder in einem serialisierten JSON-Objekt mithilfe von JSON.NET anzugeben ?
Es würde ausreichen anzugeben, dass immer zuerst ein einzelnes Feld angezeigt wird.
Gibt es eine Möglichkeit, die Reihenfolge der Felder in einem serialisierten JSON-Objekt mithilfe von JSON.NET anzugeben ?
Es würde ausreichen anzugeben, dass immer zuerst ein einzelnes Feld angezeigt wird.
Antworten:
Die unterstützte Methode besteht darin, das JsonProperty
Attribut für die Klasseneigenschaften zu verwenden, für die Sie die Reihenfolge festlegen möchten. Weitere Informationen finden Sie in der Bestelldokumentation zu JsonPropertyAttribute .
Übergeben Sie den JsonProperty
ein Order
Wert und der Serializer wird der Rest kümmern.
[JsonProperty(Order = 1)]
Dies ist sehr ähnlich zu
DataMember(Order = 1)
der System.Runtime.Serialization
Tage.
Hier ist ein wichtiger Hinweis von @ kevin-babcock
... das Setzen der Reihenfolge auf 1 funktioniert nur, wenn Sie für alle anderen Eigenschaften eine Reihenfolge größer als 1 festlegen. Standardmäßig erhält jede Eigenschaft ohne Auftragseinstellung den Auftrag -1. Sie müssen also entweder alle serialisierten Eigenschaften und die Reihenfolge angeben oder Ihren ersten Artikel auf -2 setzen
Order
Eigenschaft von JsonPropertyAttribute
kann die Reihenfolge gesteuert werden, in der Felder serialisiert / deserialisiert werden. Das Festlegen der Reihenfolge auf 1 funktioniert jedoch nur, wenn Sie für alle anderen Eigenschaften eine Reihenfolge größer als 1 festlegen. Standardmäßig erhält jede Eigenschaft ohne Auftragseinstellung den Auftrag -1. Sie müssen also entweder alle serialisierten Eigenschaften und die Reihenfolge angeben oder Ihren ersten Artikel auf -2 setzen.
JavaScriptSerializer
.
Sie können steuern , tatsächlich die Reihenfolge durch die Implementierung IContractResolver
oder das übergeordnete DefaultContractResolver
‚s - CreateProperties
Methode.
Hier ist ein Beispiel für meine einfache Implementierung, bei IContractResolver
der die Eigenschaften alphabetisch geordnet sind:
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();
}
}
Legen Sie dann die Einstellungen fest und serialisieren Sie das Objekt. Die JSON-Felder werden in alphabetischer Reihenfolge angezeigt:
var settings = new JsonSerializerSettings()
{
ContractResolver = new OrderedContractResolver()
};
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
In meinem Fall hat Mattias 'Antwort nicht funktioniert. Die CreateProperties
Methode wurde nie aufgerufen.
Nach einigem Debuggen von Newtonsoft.Json
Interna habe ich eine andere Lösung gefunden.
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;
}
}
In meinem Fall hat die Lösung von niaher nicht funktioniert, da sie keine Objekte in Arrays verarbeitet hat.
Basierend auf seiner Lösung habe ich mir das ausgedacht
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;
}
}
}
Wie Charlie bemerkte, können Sie die Reihenfolge der JSON-Eigenschaften etwas steuern, indem Sie die Eigenschaften in der Klasse selbst anordnen. Leider funktioniert dieser Ansatz nicht für Eigenschaften, die von einer Basisklasse geerbt wurden. Die Eigenschaften der Basisklasse werden so angeordnet, wie sie im Code angeordnet sind, werden jedoch vor den Eigenschaften der Basisklasse angezeigt.
Und für alle, die sich fragen, warum Sie JSON-Eigenschaften alphabetisch sortieren möchten, ist es viel einfacher, mit JSON-Rohdateien zu arbeiten, insbesondere für Klassen mit vielen Eigenschaften, wenn diese geordnet sind.
Dies funktioniert auch für normale Klassen, Wörterbücher und ExpandoObject (dynamisches Objekt).
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
wird während der Serialisierung eines Wörterbuchs nicht aufgerufen. Ich habe das JSON.net-Repo untersucht, um herauszufinden, welche Maschinen tatsächlich durch die Wörterbucheinträge tuckern. Es lässt sich nicht in eine override
oder andere Anpassung für die Bestellung einbinden. Es werden nur die Einträge wie sie sind aus dem Enumerator des Objekts übernommen. Es scheint, dass ich ein SortedDictionary
oder SortedList
erstellen muss, um JSON.net dazu zu zwingen. Feature-Vorschlag eingereicht: github.com/JamesNK/Newtonsoft.Json/issues/2270
Wenn Sie nicht JsonProperty
Order
jeder Klasseneigenschaft ein Attribut zuweisen möchten, können Sie ganz einfach Ihren eigenen ContractResolver erstellen ...
Über die IContractResolver-Schnittstelle können Sie anpassen, wie der JsonSerializer .NET-Objekte in JSON serialisiert und deserialisiert, ohne Attribute für Ihre Klassen zu platzieren.
So was:
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;
}
}
Implementieren:
var settings = new JsonSerializerSettings { ContractResolver = SortedPropertiesContractResolver.Instance };
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
Die folgende rekursive Methode verwendet Reflektion, um die interne Token-Liste für eine vorhandene JObject
Instanz zu sortieren, anstatt ein brandneues sortiertes Objektdiagramm zu erstellen. Dieser Code basiert auf internen Json.NET-Implementierungsdetails und sollte nicht in der Produktion verwendet werden.
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);
}
}
Da mein Objekt bereits ein JObject war, habe ich die folgende Lösung verwendet:
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);
}
}
und dann benutze es so:
string serializedObj = JsonConvert.SerializeObject(new SortedJObject(dataObject));
Ich möchte ein kombiniertes Objekt serialisieren und die Reihenfolge der Eigenschaften beibehalten, wie sie im Code definiert wurden. Ich kann nicht einfach hinzufügen, [JsonProperty(Order = 1)]
da die Klasse selbst außerhalb meines Bereichs liegt.
Diese Lösung berücksichtigt auch, dass Eigenschaften, die in einer Basisklasse definiert sind, eine höhere Priorität haben sollten.
Dies ist möglicherweise nicht kugelsicher, da nirgends definiert ist, dass MetaDataAttribute
die richtige Reihenfolge gewährleistet ist, aber es scheint zu funktionieren. Für meinen Anwendungsfall ist dies in Ordnung. da ich nur die menschliche Lesbarkeit für eine automatisch generierte Konfigurationsdatei beibehalten möchte.
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();
}
}
Wenn Sie Ihre API global mit geordneten Feldern konfigurieren möchten, kombinieren Sie bitte die Antwort von 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();
}
}
mit meiner Antwort hier:
Wie kann ich die ASP.NET-Web-API zwingen, immer JSON zurückzugeben?
AKTUALISIEREN
Ich habe gerade die Abstimmungen gesehen. Bitte lesen Sie die Antwort von 'Steve' unten, wie das geht.
ORIGINAL
Ich folgte dem JsonConvert.SerializeObject(key)
Methodenaufruf über Reflection (wobei der Schlüssel eine IList war) und stellte fest, dass JsonSerializerInternalWriter.SerializeList aufgerufen wird. Es nimmt eine Liste und durchläuft via
for (int i = 0; i < values.Count; i++) { ...
Dabei ist values der eingegebene IList-Parameter.
Die kurze Antwort lautet ... Nein, es gibt keine integrierte Möglichkeit, die Reihenfolge festzulegen, in der die Felder in der JSON-Zeichenfolge aufgeführt sind.
Es gibt keine Reihenfolge der Felder im JSON-Format, daher ist das Definieren einer Reihenfolge nicht sinnvoll.
{ id: 1, name: 'John' }
ist äquivalent zu { name: 'John', id: 1 }
(beide repräsentieren eine streng äquivalente Objektinstanz)