web-dev-qa-db-fra.com

Les éléments de l'objet JSON sont hors d'usage avec "json.dumps"?

J'utilise json.dumps pour 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: identifiant, nom, fuseau horaire - mais à la place j'ai fuseau horaire, identifiant, nom.

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

131
Noor

Les objets Python dict (avant Python 3.7]) et JSON sont des collections non ordonnées. Vous pouvez passer le paramètre sort_keys, 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'un ordre particulier; vous pourriez tilisez collections.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 du mot clé est conservé et le texte ci-dessus 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 tant que JSON, pour conserver l'ordre (pour 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)])
205
jfs

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 aux pythons récents, ou vous pouvez utiliser ceci: http://code.activestate.com/recipes/576693/ ).

Je pense que les nouvelles implémentations json de pythons gèrent correctement les commandes intégrées dans OrderedDicts, mais je ne suis pas sûr (et je n’ai pas facilement accès au test).

Les anciennes implémentations simplejson de pythons ne gèrent pas bien les objets OrderedDict et les convertissent en dict ordinaires avant de les afficher en sortie. Vous pouvez toutefois résoudre ce problème 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 ceci 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 vous le souhaitez.

Une autre solution consisterait à spécialiser l'encodeur pour qu'il utilise directement votre classe de ligne, sans avoir besoin d'aucun dict intermédiaire ni de UnorderedDict.

20
Michael Anderson

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

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

En effet, le dictionnaire était "à l'envers" avant même d'atteindre json.dumps:

>>> {"id":1,"name":"David","timezone":3}
{'timezone': 3, 'id': 1, 'name': 'David'}
7
David Robinson

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

4
Paulpro

hé, je sais qu’il est si tard pour cette réponse, mais ajoutez sort_keys et attribuez-lui la valeur false comme suit:

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

cela a fonctionné pour moi

2
Rabiea Ez Eldeen

json.dump () préservera l'ordre de votre dictionnaire. Ouvrez le fichier dans un éditeur de texte et vous verrez. Il préservera 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 demandiez de charger dans OrderedDict (), ce qui est fait avec le paramètre object_pairs_hook comme indiqué par J.F.Sebastian.

Sinon, la commande serait perdue car, dans les conditions habituelles, l’objet dictionnaire sauvegardé serait chargé dans un dict normal et un dict normal ne conservait pas l’ordre des éléments qui lui étaient donnés.

2
markling