wake-up-neo.net

Reihenfolge der serialisierten Felder mit JSON.NET

Gibt es eine Möglichkeit, die Reihenfolge der Felder in einem serialisierten JSON-Objekt mithilfe von JSON.NET anzugeben?

Es reicht aus, anzugeben, dass ein einzelnes Feld immer zuerst angezeigt wird.

101
Kevin Montrose

Die unterstützte Methode ist die Verwendung des JsonProperty-Attributs für die Klasseneigenschaften, für die Sie die Reihenfolge festlegen möchten. Weitere Informationen finden Sie in der JsonPropertyAttribute-Bestelldokumentation .

Übergeben Sie die Variable JsonProperty und Order und der Serializer kümmert sich um den Rest.

 [JsonProperty(Order = 1)]

Das ist dem sehr ähnlich 

 DataMember(Order = 1) 

der System.Runtime.Serialization Tage.

Hier ist ein wichtiger Hinweis von @ kevin-babcock

Das Festlegen der Reihenfolge auf 1 funktioniert nur, wenn Sie eine Reihenfolge größer als 1 für alle anderen Eigenschaften festlegen. Standardmäßig wird jeder Eigenschaft ohne Auftragseinstellung die Reihenfolge -1 zugewiesen. Sie müssen also entweder alle serialisierten Eigenschaften und die Reihenfolge angeben oder Ihren ersten Artikel auf -2 setzen

203
Steve

Sie können die Reihenfolge tatsächlich steuern, indem Sie IContractResolver implementieren oder die DefaultContractResolver-Methode von CreateProperties überschreiben.

Hier ist ein Beispiel meiner einfachen Implementierung von IContractResolver, die die Eigenschaften alphabetisch sortiert:

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);
112

In meinem Fall hat die Antwort von Mattias 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;
    }
}
16
niaher

In meinem Fall funktionierte die Lösung von naher nicht, weil sie keine Objekte in Arrays handhabte.

Basierend auf seiner Lösung bin ich darauf gekommen

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;
        }
    }
}
9
Tuan-Tu Tran

Wie Charlie bemerkt hat, 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 werden. Die Eigenschaften der Basisklasse werden so angeordnet, wie sie im Code angeordnet sind, erscheinen jedoch vor den Eigenschaften der Basisklasse.

Und für alle, die sich fragen, warum Sie JSON-Eigenschaften alphabetisieren möchten, ist die Arbeit mit rohen JSON-Dateien viel einfacher, insbesondere für Klassen mit vielen Eigenschaften, wenn diese bestellt werden.

3
Jack Bond

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);
1
Jay Shah

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 wie folgt verwenden:

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

Ich möchte ein Comblex-Objekt serialisieren und die Reihenfolge der Eigenschaften beibehalten, wie sie im Code definiert wurden. Ich kann nicht einfach [JsonProperty(Order = 1)] hinzufügen, 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 nirgendwo definiert wird, dass die MetaDataAttribute die korrekte Reihenfolge gewährleistet, aber es scheint zu funktionieren. Für meinen Anwendungsfall ist das 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();
    }
}
0

Die folgende rekursive Methode verwendet Reflection, um die interne Tokenliste in einer vorhandenen 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);
    }
}
0
Nathan Baulch

Wenn Sie die Klasse steuern (d. H. Schreiben), ordnen Sie die Eigenschaften in alphabetischer Reihenfolge an. Wenn JsonConvert.SerializeObject() aufgerufen wird, werden sie in alphabetischer Reihenfolge serialisiert.

0
Charlie