Je pense qu'il est probablement intéressé à montrer d'abord le champ ID (ou similaire), puis tous les autres champs. c'est plus convivial pour les utilisateurs finaux que de le rechercher après des champs commençant par A..I
Michael Bahig
3
Les propriétés JSON sont définies comme non ordonnées. Je pense que c'est tout à fait correct de forcer un ordre de SORTIE particulier pendant la sérialisation (pour le plaisir de regarder le JSON peut-être), mais ce serait une mauvaise décision de créer une DÉPENDANCE sur un ordre particulier de désérialisation.
DaBlick
5
Quelques raisons valables: (1) simuler une propriété "$ type" qui doit être la première propriété du JSON, (2) essayer de générer un JSON qui compresse autant que possible
Stephen Chung
4
Une autre raison pourrait être (3) une représentation canonique qui utilise la syntaxe JSON - le même objet doit être garanti pour produire la même chaîne JSON. Un ordre déterministe des attributs est une condition préalable nécessaire pour cela.
MarkusSchaber
2
Kevin, pouvez-vous mettre à jour la réponse acceptée à cette question?
Millie Smith
Réponses:
256
La méthode prise en charge consiste à utiliser l' JsonPropertyattribut sur les propriétés de classe pour lesquelles vous souhaitez définir l'ordre. Lisez la documentation de la commande JsonPropertyAttribute pour plus d'informations.
Passez la valeur JsonPropertyan Orderet le sérialiseur s'occupera du reste.
[JsonProperty(Order=1)]
Ceci est très similaire au
DataMember(Order=1)
des System.Runtime.Serializationjours.
Voici une note importante de @ kevin-babcock
... définir l'ordre sur 1 ne fonctionnera que si vous définissez un ordre supérieur à 1 sur toutes les autres propriétés. Par défaut, toute propriété sans paramètre Order recevra un ordre de -1. Vous devez donc soit donner toutes les propriétés sérialisées et commander, soit définir votre premier élément sur -2
L'utilisation de la Orderpropriété de JsonPropertyAttributepeut être utilisée pour contrôler l'ordre dans lequel les champs sont sérialisés / désérialisés. Cependant, définir l'ordre sur 1 ne fonctionnera que si vous définissez un ordre supérieur à 1 sur toutes les autres propriétés. Par défaut, toute propriété sans paramètre Order recevra un ordre de -1. Vous devez donc soit donner toutes les propriétés sérialisées et l'ordre, soit définir votre premier élément sur -2.
Kevin Babcock
1
Cela fonctionne pour la sérialisation, mais la commande n'est pas prise en compte lors de la désérialisation. Selon la documentation, l'attribut order est utilisé à la fois pour la sérialisation et la désérialisation. Y at-il un travail autour?
cangosta
1
Existe-t-il une propriété similaire pour le JavaScriptSerializer.
Shimmy Weitzhandler
4
@cangosta L'ordre de désérialisation ne devrait pas avoir d'importance .. sauf dans certains cas d'attente très "étranges".
user2864740
1
Lisez la discussion similaire sur les problèmes de github autour du désir que l'Ordre soit respecté dans la désérialisation: github.com/JamesNK/Newtonsoft.Json/issues/758 Fondamentalement, aucune chance de celui-ci.
Tyeth
126
Vous pouvez en fait contrôler l'ordre en implémentant IContractResolverou en remplaçant la méthode DefaultContractResolvers CreateProperties.
Voici un exemple de ma mise en œuvre simple IContractResolverqui classe les propriétés par ordre alphabétique:
C'est très utile (+1) mais une mise en garde: il semble que la sérialisation des dictionnaires n'utilise pas cette personnalisation CreateProperties. Ils sérialisent bien mais ne finissent pas par être triés. Je suppose qu'il existe une manière différente de personnaliser la sérialisation des dictionnaires, mais je ne l'ai pas trouvée.
solublefish
Parfait. Fait exactement ce que je voulais. Merci.
Wade Hatler
C'est une excellente solution. Cela a parfaitement fonctionné pour moi, en particulier lors de la mise côte à côte de 2 objets JSON et de l'alignement des propriétés.
Vince
16
Dans mon cas, la réponse de Mattias n'a pas fonctionné. La CreatePropertiesméthode n'a jamais été appelée.
Après quelques débogages des Newtonsoft.Jsoninternes, j'ai trouvé une autre solution.
publicclassJsonUtility{publicstaticstringNormalizeJsonString(string json){// Parse json string into JObject.var parsedObject =JObject.Parse(json);// Sort properties of JObject.var normalizedObject =SortPropertiesAlphabetically(parsedObject);// Serialize JObject .returnJsonConvert.SerializeObject(normalizedObject);}privatestaticJObjectSortPropertiesAlphabetically(JObject original){var result =newJObject();foreach(var property in original.Properties().ToList().OrderBy(p => p.Name)){varvalue= property.ValueasJObject;if(value!=null){value=SortPropertiesAlphabetically(value);
result.Add(property.Name,value);}else{
result.Add(property.Name, property.Value);}}return result;}}
C'était le correctif requis pour nous lors de l'utilisation de dictionnaires.
noocyte
Cela ajoute une surcharge de désérialisation et de sérialisation supplémentaires. J'ai ajouté une solution qui fonctionnera également pour les classes normales, les dictionnaires et ExpandoObject (objet dynamique)
Jay Shah
11
Dans mon cas, la solution de niaher n'a pas fonctionné car elle ne gérait pas les objets dans les tableaux.
Sur la base de sa solution, c'est ce que j'ai proposé
Cela ajoute une surcharge de désérialisation et de sérialisation supplémentaires.
Jay Shah
Excellente solution. Je vous remercie.
MaYaN
3
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 classées telles qu'elles sont présentées dans le code, mais apparaîtront avant les propriétés de la classe de base.
Et pour tous ceux qui se demandent pourquoi vous souhaitez classer par ordre alphabétique les propriétés JSON, il est beaucoup plus facile de travailler avec des fichiers JSON bruts, en particulier pour les classes avec de nombreuses propriétés, si elles sont ordonnées.
N'était-ce pas le comportement de classement par défaut lors de la sérialisation?
mr5
1
Pour économiser à quelqu'un d'autre quelques minutes perdues, notez que cette réponse ne fonctionne pas pour les dictionnaires malgré la revendication. CreatePropertiesn'est pas appelée lors de la sérialisation d'un dictionnaire. J'ai exploré le repo JSON.net pour savoir quelles machines sont réellement en train de parcourir les entrées du dictionnaire. Il ne se connecte à aucune overridepersonnalisation pour la commande. Il prend simplement les entrées telles quelles de l'énumérateur de l'objet. Il semble que je doive construire un SortedDictionaryou SortedListpour forcer JSON.net à le faire. Suggestion de fonctionnalité déposée: github.com/JamesNK/Newtonsoft.Json/issues/2270
William
2
Si vous ne voulez pas mettre un JsonPropertyOrderattribut sur chaque propriété de classe, il est très simple de créer votre propre ContractResolver ...
L'interface IContractResolver fournit un moyen de personnaliser la façon dont JsonSerializer sérialise et désérialise les objets .NET en JSON sans placer d'attributs sur vos classes.
Comme ça:
privateclassSortedPropertiesContractResolver:DefaultContractResolver{// use a static instance for optimal performancestaticSortedPropertiesContractResolver instance;staticSortedPropertiesContractResolver(){ instance =newSortedPropertiesContractResolver();}publicstaticSortedPropertiesContractResolverInstance{get{return instance;}}protectedoverrideIList<JsonProperty>CreateProperties(Type type,MemberSerialization memberSerialization){var properties =base.CreateProperties(type, memberSerialization);if(properties !=null)return properties.OrderBy(p => p.UnderlyingName).ToList();return properties;}}
Mettre en place:
var settings =newJsonSerializerSettings{ContractResolver=SortedPropertiesContractResolver.Instance};var json =JsonConvert.SerializeObject(obj,Formatting.Indented, settings);
La méthode récursive suivante utilise la réflexion pour trier la liste de jetons interne sur une JObjectinstance existante plutôt que de créer un tout nouveau graphique d'objets triés. Ce code s'appuie sur les détails d'implémentation Json.NET internes et ne doit pas être utilisé en production.
voidSortProperties(JToken token){var obj = token asJObject;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(newComparisonComparer((x, y)=>{var xProp = x asJProperty;var yProp = y asJProperty;return xProp !=null&& yProp !=null?string.Compare(xProp.Name, yProp.Name):0;}));}foreach(var child in token.Children()){SortProperties(child);}}
Si vous contrôlez (c'est-à-dire écrivez) la classe, placez les propriétés par ordre alphabétique et elles seront sérialisées par ordre alphabétique lors de l' JsonConvert.SerializeObject()appel.
Je veux sérialiser un objet comblex et conserver l'ordre des propriétés telles qu'elles ont été définies dans le code. Je ne peux pas simplement ajouter [JsonProperty(Order = 1)]parce que la classe elle-même est hors de ma portée.
Cette solution prend également en compte le fait que les propriétés définies dans une classe de base doivent avoir une priorité plus élevée.
Cela n'est peut-être pas à toute épreuve, car nulle part n'est défini qui MetaDataAttributegarantit le bon ordre, mais cela semble fonctionner. Pour mon cas d'utilisation, c'est ok. puisque je veux seulement maintenir la lisibilité humaine pour un fichier de configuration généré automatiquement.
publicclassPersonWithAge:Person{publicintAge{get;set;}}publicclassPerson{publicstringName{get;set;}}publicstringGetJson(){var thequeen =newPersonWithAge{Name="Elisabeth",Age=Int32.MaxValue};var settings =newJsonSerializerSettings(){ContractResolver=newMetadataTokenContractResolver(),};returnJsonConvert.SerializeObject(
thequeen,Newtonsoft.Json.Formatting.Indented, settings
);}publicclassMetadataTokenContractResolver:DefaultContractResolver{protectedoverrideIList<JsonProperty>CreateProperties(Type type,MemberSerialization memberSerialization){var props = type
.GetProperties(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic).ToDictionary(k => k.Name, v =>{// first value: declaring typevar classIndex =0;var t = type;while(t != v.DeclaringType){
classIndex++;
t = type.BaseType;}returnTuple.Create(classIndex, v.MetadataToken);});returnbase.CreateProperties(type, memberSerialization).OrderByDescending(p => props[p.PropertyName].Item1).ThenBy(p => props[p.PropertyName].Item1).ToList();}}
Je viens de voir les votes négatifs. Veuillez consulter la réponse de «Steve» ci-dessous pour savoir comment procéder.
ORIGINAL
J'ai suivi l' JsonConvert.SerializeObject(key)appel de méthode via la réflexion (où la clé était un IList) et j'ai trouvé que JsonSerializerInternalWriter.SerializeList était 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 moyen intégré de définir l'ordre dans lequel les champs sont répertoriés dans la chaîne JSON.
@Darin - mais il y a un ordre dans la sérialisation. "{id: 1, name: 'John'}" et "{name: 'John', id: 1}" sont différents en tant que chaînes , c'est ce qui m'intéresse ici. Bien entendu, les objets sont équivalents lorsqu'ils sont désérialisés.
Kevin Montrose
1
@Darin - non, pas dans ce cas. Je sérialise quelque chose, puis je le transmets sous forme de chaîne à un service qui ne traite que des chaînes (pas compatible JSON), et il serait pratique pour diverses raisons qu'un champ apparaisse en premier dans la chaîne.
Kevin Montrose
1
c'est bon pour les tests aussi, pouvoir simplement regarder les chaînes plutôt que d'avoir à désérialiser.
Steve
9
Un ordre de sérialisation stable est également pratique pour la validation du cache. Il est trivial de prendre une somme de contrôle d'une chaîne - pas vrai pour un graphe d'objets complet.
solublefish
1
L'ordre de sérialisation est également pratique lorsque vous effectuez des tests unitaires afin que vous puissiez facilement dire que les chaînes de réponse attendues et réelles sont égales même lorsque l'ordre des propriétés json est différent.
Réponses:
La méthode prise en charge consiste à utiliser l'
JsonProperty
attribut sur les propriétés de classe pour lesquelles vous souhaitez définir l'ordre. Lisez la documentation de la commande JsonPropertyAttribute pour plus d'informations.Passez la valeur
JsonProperty
anOrder
et le sérialiseur s'occupera du reste.Ceci est très similaire au
des
System.Runtime.Serialization
jours.Voici une note importante de @ kevin-babcock
la source
Order
propriété deJsonPropertyAttribute
peut être utilisée pour contrôler l'ordre dans lequel les champs sont sérialisés / désérialisés. Cependant, définir l'ordre sur 1 ne fonctionnera que si vous définissez un ordre supérieur à 1 sur toutes les autres propriétés. Par défaut, toute propriété sans paramètre Order recevra un ordre de -1. Vous devez donc soit donner toutes les propriétés sérialisées et l'ordre, soit définir votre premier élément sur -2.JavaScriptSerializer
.Vous pouvez en fait contrôler l'ordre en implémentant
IContractResolver
ou en remplaçant la méthodeDefaultContractResolver
sCreateProperties
.Voici un exemple de ma mise en œuvre simple
IContractResolver
qui classe les propriétés par ordre alphabétique:Et puis définissez les paramètres et sérialisez l'objet, et les champs JSON seront dans l'ordre alphabétique:
la source
Dans mon cas, la réponse de Mattias n'a pas fonctionné. La
CreateProperties
méthode n'a jamais été appelée.Après quelques débogages des
Newtonsoft.Json
internes, j'ai trouvé une autre solution.la source
Dans mon cas, la solution de niaher n'a pas fonctionné car elle ne gérait pas les objets dans les tableaux.
Sur la base de sa solution, c'est ce que j'ai proposé
la source
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 classées telles qu'elles sont présentées dans le code, mais apparaîtront avant les propriétés de la classe de base.
Et pour tous ceux qui se demandent pourquoi vous souhaitez classer par ordre alphabétique les propriétés JSON, il est beaucoup plus facile de travailler avec des fichiers JSON bruts, en particulier pour les classes avec de nombreuses propriétés, si elles sont ordonnées.
la source
Cela fonctionnera également pour les classes normales, les dictionnaires et ExpandoObject (objet dynamique).
la source
CreateProperties
n'est pas appelée lors de la sérialisation d'un dictionnaire. J'ai exploré le repo JSON.net pour savoir quelles machines sont réellement en train de parcourir les entrées du dictionnaire. Il ne se connecte à aucuneoverride
personnalisation pour la commande. Il prend simplement les entrées telles quelles de l'énumérateur de l'objet. Il semble que je doive construire unSortedDictionary
ouSortedList
pour forcer JSON.net à le faire. Suggestion de fonctionnalité déposée: github.com/JamesNK/Newtonsoft.Json/issues/2270Si vous ne voulez pas mettre un
JsonProperty
Order
attribut sur chaque propriété de classe, il est très simple de créer votre propre ContractResolver ...Comme ça:
Mettre en place:
la source
La méthode récursive suivante utilise la réflexion pour trier la liste de jetons interne sur une
JObject
instance existante plutôt que de créer un tout nouveau graphique d'objets triés. Ce code s'appuie sur les détails d'implémentation Json.NET internes et ne doit pas être utilisé en production.la source
En fait, comme mon Object était déjà un JObject, j'ai utilisé la solution suivante:
puis utilisez-le comme ceci:
la source
Si vous contrôlez (c'est-à-dire écrivez) la classe, placez les propriétés par ordre alphabétique et elles seront sérialisées par ordre alphabétique lors de l'
JsonConvert.SerializeObject()
appel.la source
Je veux sérialiser un objet comblex et conserver l'ordre des propriétés telles qu'elles ont été définies dans le code. Je ne peux pas simplement ajouter
[JsonProperty(Order = 1)]
parce que la classe elle-même est hors de ma portée.Cette solution prend également en compte le fait que les propriétés définies dans une classe de base doivent avoir une priorité plus élevée.
Cela n'est peut-être pas à toute épreuve, car nulle part n'est défini qui
MetaDataAttribute
garantit le bon ordre, mais cela semble fonctionner. Pour mon cas d'utilisation, c'est ok. puisque je veux seulement maintenir la lisibilité humaine pour un fichier de configuration généré automatiquement.la source
Si vous souhaitez configurer globalement votre API avec des champs ordonnés, veuillez combiner la réponse de Mattias Nordberg:
avec ma réponse ici:
Comment forcer l'API Web ASP.NET à toujours renvoyer JSON?
la source
METTRE À JOUR
Je viens de voir les votes négatifs. Veuillez consulter la réponse de «Steve» ci-dessous pour savoir comment procéder.
ORIGINAL
J'ai suivi l'
JsonConvert.SerializeObject(key)
appel de méthode via la réflexion (où la clé était un IList) et j'ai trouvé que JsonSerializerInternalWriter.SerializeList était appelé. Il prend une liste et boucle viafor (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 moyen intégré de définir l'ordre dans lequel les champs sont répertoriés dans la chaîne JSON.
la source
Il n'y a pas d'ordre des champs au format JSON, donc définir un ordre n'a pas de sens.
{ id: 1, name: 'John' }
est équivalent à{ name: 'John', id: 1 }
(les deux représentent une instance d'objet strictement équivalente)la source