Ordre des champs sérialisés en utilisant JSON.NET

est-il possible de spécifier l'ordre des champs dans un objet JSON sérialisé en utilisant JSON.NET ?

, Il suffirait de préciser qu'un seul champ apparaissent toujours en premier.

91
demandé sur vaxquis 2010-07-26 01:01:20

11 réponses

j'ai suivi l'appel de méthode JsonConvert.SerializeObject(key) via la réflexion (où key était un IList) et j'ai trouvé que JsonSerializerInternalWriter.SerializeList est appelé. Il prend une liste et boucle via

for (int i = 0; i < values.Count; i++) { ...

où values est le paramètre IList introduit.

la réponse courte est...non, il n'y a pas de construit de manière à définir l'ordre les champs sont énumérés dans la chaîne JSON.

-2
répondu DougJones 2010-08-06 16:11:17

la manière supportée est d'utiliser l'attribut JsonProperty sur les propriétés de classe pour lesquelles vous voulez définir l'ordre. lisez la documentation de commande de JsonPropertyAttribute pour plus d'information .

passer le JsonProperty un Order valeur et le serialiseur prendra soin du reste.

 [JsonProperty(Order = 1)]

ce qui est très similaire à la

 DataMember(Order = 1) 

des jours System.Runtime.Serialization .

184
répondu Steve 2017-08-10 21:05:51

vous pouvez effectivement contrôler l'ordre en implémentant IContractResolver ou en remplaçant la méthode DefaultContractResolver 's CreateProperties .

voici un exemple de mon implémentation simple de IContractResolver qui commande les propriétés par ordre alphabétique:

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();
    }
}

et ensuite définir les paramètres et sérialiser l'objet, et les champs JSON seront dans l'ordre alphabétique:

var settings = new JsonSerializerSettings()
{
    ContractResolver = new OrderedContractResolver()
};

var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
101
répondu Mattias Nordberg 2012-07-03 10:34:58

dans mon cas, la réponse de Mattias n'a pas fonctionné. La méthode CreateProperties n'a jamais été appelée.

après quelques déboguages de Newtonsoft.Json internes, j'ai trouvé une autre solution.

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;
    }
}
14
répondu niaher 2015-02-17 07:47:45

dans mon cas la solution de niaher n'a pas fonctionné parce qu'elle ne manipulait pas les objets dans les tableaux.

sur la base de sa solution c'est ce que j'ai trouvé

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
répondu Tuan-Tu Tran 2016-11-18 14:16:42

comme Charlie l'a noté, vous pouvez quelque peu contrôler l'ordre des propriétés JSON en ordonnant les propriétés dans la classe elle-même. Malheureusement, cette approche ne fonctionne pas pour les propriétés héritées d'une classe de base. Les propriétés de la classe de base seront ordonnées comme elles sont présentées dans le code, mais apparaîtront avant les propriétés de la classe de base.

et pour ceux qui se demandent pourquoi vous pourriez vouloir classer les propriétés JSON par ordre alphabétique, c'est beaucoup plus facile de travailler avec JSON brut fichiers, en particulier pour les classes avec beaucoup de propriétés, si elles sont ordonnées.

2
répondu Jack Bond 2017-04-02 23:03:34

la méthode récursive suivante utilise la réflexion pour trier la liste interne des tokens sur une instance JObject existante plutôt que de créer un tout nouveau graphe d'objet trié. Ce code repose sur des Json.NET détails de la mise en œuvre et ne doit pas être utilisé dans la production.

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
répondu Nathan Baulch 2012-07-06 11:40:34

en fait, puisque mon objet était déjà un objet de travail, j'ai utilisé la solution suivante:

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

et puis l'utiliser comme ceci:

string serializedObj = JsonConvert.SerializeObject(new SortedJObject(dataObject));
0
répondu Danny R 2016-06-08 22:49:09

si vous contrôlez (c.-à-D. Écrivez) la classe, mettez les propriétés dans l'ordre alphabétique et ils serialiseront dans l'ordre alphabétique quand JsonConvert.SerializeObject() est appelé.

0
répondu Charlie 2016-11-27 20:15:08

si vous souhaitez configurer globalement votre API avec des champs commandés, veuillez combiner la réponse de 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();
    }
}

avec ma réponse ici:

comment forcer ASP.NET API Web pour toujours retourner JSON?

0
répondu Carlo Saccone 2018-09-26 11:17:09

il n'y a pas d'ordre de champs dans le format JSON, donc définir un ordre n'a pas de sens.

{ id: 1, name: 'John' } est équivalent à { name: 'John', id: 1 } (tous deux représentent une instance d'objet strictement équivalente)

-3
répondu Darin Dimitrov 2010-07-25 21:08:24