Les éléments de l'objet JSON sont dans le désordre en utilisant «json.dumps»?

157

J'utilise json.dumpspour convertir en json comme

countries.append({"id":row.id,"name":row.name,"timezone":row.timezone})
print json.dumps(countries)

Le résultat que j'ai est:

[
   {"timezone": 4, "id": 1, "name": "Mauritius"}, 
   {"timezone": 2, "id": 2, "name": "France"}, 
   {"timezone": 1, "id": 3, "name": "England"}, 
   {"timezone": -4, "id": 4, "name": "USA"}
]

Je veux avoir les clés dans l'ordre suivant: id, nom, fuseau horaire - mais à la place j'ai le fuseau horaire, id, nom.

Comment dois-je résoudre ce problème?

Noor
la source

Réponses:

244

Les dictobjets Python (avant Python 3.7) et JSON sont des collections non ordonnées. Vous pouvez passer le sort_keysparamètre, pour trier les clés:

>>> import json
>>> json.dumps({'a': 1, 'b': 2})
'{"b": 2, "a": 1}'
>>> json.dumps({'a': 1, 'b': 2}, sort_keys=True)
'{"a": 1, "b": 2}'

Si vous avez besoin d'une commande particulière; vous pouvez utilisercollections.OrderedDict :

>>> from collections import OrderedDict
>>> json.dumps(OrderedDict([("a", 1), ("b", 2)]))
'{"a": 1, "b": 2}'
>>> json.dumps(OrderedDict([("b", 2), ("a", 1)]))
'{"b": 2, "a": 1}'

Depuis Python 3.6 , l'ordre des arguments des mots clés est conservé et ce qui précède peut être réécrit en utilisant une syntaxe plus agréable:

>>> json.dumps(OrderedDict(a=1, b=2))
'{"a": 1, "b": 2}'
>>> json.dumps(OrderedDict(b=2, a=1))
'{"b": 2, "a": 1}'

Voir PEP 468 - Préservation de l'ordre des arguments de mots-clés .

Si votre entrée est donnée en JSON alors pour conserver l'ordre (à obtenir OrderedDict), vous pouvez passer object_pair_hook, comme suggéré par @Fred Yankowski :

>>> json.loads('{"a": 1, "b": 2}', object_pairs_hook=OrderedDict)
OrderedDict([('a', 1), ('b', 2)])
>>> json.loads('{"b": 2, "a": 1}', object_pairs_hook=OrderedDict)
OrderedDict([('b', 2), ('a', 1)])
jfs
la source
2
OrdonnéDict's init really ugly
jean
3
@jean: la valeur initiale n'a rien à voir avec OrderedDict(), vous pouvez passer un dictà OrderedDict(), vous pouvez également passer une liste de paires ordonnées à dict()- bien que l'ordre soit perdu dans ces deux cas.
jfs
Je veux dire l'initier quand conserver l'ordre, doit taper plusieurs '(' et ')'
jean
@jean: il y a ordereddict_literalsdu codetransformerpackage (qualité alpha)
jfs
25
De plus, si vous chargez JSON en utilisant d = json.load(f, object_pairs_hook=OrderedDict), une version ultérieure json.dump(d)conservera l'ordre des éléments d'origine.
Fred Yankowski
21

Comme d'autres l'ont mentionné, le dict sous-jacent n'est pas ordonné. Cependant, il existe des objets OrderedDict en python. (Ils sont intégrés dans les pythons récents, ou vous pouvez utiliser ceci: http://code.activestate.com/recipes/576693/ ).

Je crois que les nouvelles implémentations de pythons json gèrent correctement les OrderedDicts intégrés, mais je ne suis pas sûr (et je n'ai pas un accès facile au test).

Les anciennes implémentations de pythons simplesjson ne gèrent pas correctement les objets OrderedDict ... et les convertissent en dictés normaux avant de les afficher ... mais vous pouvez surmonter cela en procédant comme suit:

class OrderedJsonEncoder( simplejson.JSONEncoder ):
   def encode(self,o):
      if isinstance(o,OrderedDict.OrderedDict):
         return "{" + ",".join( [ self.encode(k)+":"+self.encode(v) for (k,v) in o.iteritems() ] ) + "}"
      else:
         return simplejson.JSONEncoder.encode(self, o)

maintenant en utilisant cela, nous obtenons:

>>> import OrderedDict
>>> unordered={"id":123,"name":"a_name","timezone":"tz"}
>>> ordered = OrderedDict.OrderedDict( [("id",123), ("name","a_name"), ("timezone","tz")] )
>>> e = OrderedJsonEncoder()
>>> print e.encode( unordered )
{"timezone": "tz", "id": 123, "name": "a_name"}
>>> print e.encode( ordered )
{"id":123,"name":"a_name","timezone":"tz"}

Ce qui est à peu près comme souhaité.

Une autre alternative serait de spécialiser l'encodeur pour utiliser directement votre classe de lignes, et vous n'aurez alors pas besoin de dict intermédiaire ou de UnorderedDict.

Michael Anderson
la source
5
Notez que les objets JSON ne sont toujours pas ordonnés ; un client JSON peut lire la définition d'objet et ignorer complètement l'ordre des clés et être entièrement conforme à la RFC.
Martijn Pieters
4
Martijn a raison, cela n'affecte pas la conformité RFC, mais cela peut certainement être utile si vous voulez avoir un format cohérent pour votre JSON (par exemple si le fichier est sous contrôle de version, ou pour faciliter la tâche d'un lecteur humain comprendre, de faire correspondre l'ordre d'entrée à votre documentation.)
Michael Anderson
3
Dans ce cas , vous venez de sort_keysà Truelors de l' appel json.dumps(); pour la stabilité de l'ordre (pour les tests, la mise en cache stable ou les validations VCS), le tri des clés suffit.
Martijn Pieters
7

L'ordre d'un dictionnaire n'a aucun rapport avec l'ordre dans lequel il a été défini. Ceci est vrai pour tous les dictionnaires, pas seulement ceux qui sont convertis en JSON.

>>> {"b": 1, "a": 2}
{'a': 2, 'b': 1}

En effet, le dictionnaire a été «retourné» avant même d'atteindre json.dumps:

>>> {"id":1,"name":"David","timezone":3}
{'timezone': 3, 'id': 1, 'name': 'David'}
David Robinson
la source
6

hé je sais qu'il est trop tard pour cette réponse mais ajoutez sort_keys et attribuez-lui false comme suit:

json.dumps({'****': ***},sort_keys=False)

cela a fonctionné pour moi

Rabiea Ez Eldeen
la source
4

json.dump () conservera l'ordre de votre dictionnaire. Ouvrez le fichier dans un éditeur de texte et vous verrez. Il conservera la commande, que vous lui envoyiez ou non un OrderedDict.

Mais json.load () perdra l'ordre de l'objet enregistré à moins que vous ne lui disiez de se charger dans OrderedDict (), ce qui est fait avec le paramètre object_pairs_hook comme JFSebastian l'a indiqué ci-dessus.

Sinon, il perdrait l'ordre car, dans le cadre d'une opération habituelle, il charge l'objet dictionnaire enregistré dans un dict normal et un dict régulier ne conserve pas l'odeur des éléments qui lui sont donnés.

marquage
la source
C'est en fait une meilleure solution, car le maintien de l'ordre en charge prend en charge la commande du temps de vidage. Merci pour cette réponse.
Arun R
2

en JSON, comme en Javascript, l'ordre des clés d'objet n'a pas de sens, donc peu importe l'ordre dans lequel elles sont affichées, c'est le même objet.

Paul
la source
(et il en va de même pour un Python standard dict)
12
mais comme JSON est une représentation sous forme de chaîne jusqu'à ce qu'il soit analysé, les comparaisons de chaînes (comme dans les doctests) peuvent toujours nécessiter de l'ordre. Donc je ne dirais pas que cela n'a jamais d'importance.
Michael Scott Cuthbert
1
Bien que cela soit vrai pour la norme Javascript (script ECMA), toutes les implémentations conservent les clés (chaîne) dans l'ordre source.
thebjorn
1
@Paulpro vraiment? laquelle? Je sais que Chrome a essayé de suivre la norme ici une fois, mais a été contraint à la soumission ( code.google.com/p/v8/issues/detail?id=164 ). Je ne pensais pas que quiconque essayerait la même chose après ça ...
thebjorn
2
@paulpro vous répondez correctement à la question du PO. Je veux ajouter, cependant, qu'il existe des utilisations légitimes pour préserver l'ordre. Par exemple, on peut écrire un script qui lit JSON, applique une transformation et réécrit les résultats. Vous voudriez que l'ordre soit préservé afin qu'un outil de comparaison montre clairement les changements.
Paul Rademacher