Los elementos en el objeto JSON están fuera de servicio usando "json.dumps"?

157

Estoy usando json.dumpspara convertir en json como

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

El resultado que tengo es:

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

Quiero tener las claves en el siguiente orden: id, nombre, zona horaria, pero en cambio tengo zona horaria, id, nombre.

¿Cómo debo arreglar esto?

Noor
fuente

Respuestas:

244

Tanto Python dict(antes de Python 3.7) como el objeto JSON son colecciones desordenadas. Puede pasar el sort_keysparámetro para ordenar las claves:

>>> 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 necesita un pedido en particular; podrías usarcollections.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}'

Desde Python 3.6 , el orden del argumento de la palabra clave se conserva y lo anterior se puede reescribir usando una sintaxis más agradable:

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

Ver PEP 468 - Preservar el orden de los argumentos de palabras clave .

Si su entrada se da como JSON, para preservar el orden (para obtener OrderedDict), puede pasar object_pair_hook, como lo sugiere @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
fuente
2
El inicio de Ordictict realmente feo
jean
3
@jean: el valor inicial no tiene nada que ver OrderedDict(), puede pasar un dicta OrderedDict(), también puede pasar una lista de pares ordenados dict(), aunque el orden se pierde en ambos casos.
jfs
Me refiero a init cuando preservar el orden, necesita escribir muchos '(' y ')'
jean
@jean: hay ordereddict_literalsdel codetransformerpaquete (calidad alfa)
jfs
25
Además, si carga JSON usando d = json.load(f, object_pairs_hook=OrderedDict), un posterior json.dump(d)conservará el orden de los elementos originales.
Fred Yankowski
21

Como otros han mencionado, el dict subyacente no está ordenado. Sin embargo, hay objetos OrderedDict en python. (Están construidos en pitones recientes, o puede usar esto: http://code.activestate.com/recipes/576693/ ).

Creo que las implementaciones más recientes de pythons json manejan correctamente los OrderedDicts incorporados, pero no estoy seguro (y no tengo acceso fácil para probar).

Las implementaciones antiguas de pythons simplejson no manejan bien los objetos OrderedDict ... y los convierten en dictos regulares antes de generarlos ... pero puede superar esto haciendo lo siguiente:

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)

ahora usando esto obtenemos:

>>> 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"}

Lo cual es más o menos como se desea.

Otra alternativa sería especializar el codificador para usar directamente su clase de fila, y luego no necesitaría ningún dict intermedio o UnorderedDict.

Michael Anderson
fuente
55
Tenga en cuenta que los objetos JSON aún no están ordenados ; un cliente JSON puede leer la definición del objeto e ignorar por completo el orden de las claves y ser totalmente compatible con RFC.
Martijn Pieters
44
Martijn está en lo correcto, esto no afecta el cumplimiento de RFC, pero ciertamente puede ser valioso si desea tener un formato consistente para su JSON (por ejemplo, si el archivo está bajo control de versión, o para que sea más fácil para un lector humano comprender, hacer que la orden de entrada coincida con su documentación.)
Michael Anderson
3
En cuyo caso que acaba de establecer sort_keysque Truecuando se llama json.dumps(); para la estabilidad del pedido (para pruebas, almacenamiento en caché estable o confirmaciones de VCS), la clasificación de claves es suficiente.
Martijn Pieters
7

El orden de un diccionario no tiene ninguna relación con el orden en que se definió. Esto es cierto para todos los diccionarios, no solo para aquellos convertidos en JSON.

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

De hecho, el diccionario se puso "al revés" incluso antes de llegar a json.dumps:

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

Hola, sé que es muy tarde para esta respuesta, pero agregue sort_keys y asigne falso de la siguiente manera:

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

esto funcionó para mí

Rabiea Ez Eldeen
fuente
4

json.dump () preservará el orden de su diccionario. Abra el archivo en un editor de texto y verá. Conservará el pedido independientemente de si le envía un OrderedDict.

Pero json.load () perderá el orden del objeto guardado a menos que le diga que se cargue en un OrderedDict (), lo que se hace con el parámetro object_pairs_hook como JFSebastian instruyó anteriormente.

De lo contrario, perdería el orden porque, en la operación habitual, carga el objeto de diccionario guardado en un dict regular y un dict regular no conserva el orden de los elementos que se le dan.

Markling
fuente
Esto es en realidad una mejor solución ya que mantener el orden en la carga se encarga de ordenar el tiempo de descarga. Gracias por esta respuesta
Arun R
2

en JSON, como en Javascript, el orden de las claves de objeto no tiene sentido, por lo que realmente no importa en qué orden se muestren, es el mismo objeto.

Pablo
fuente
(y lo mismo también es cierto para un Python estándar dict)
12
pero dado que JSON es una representación de cadena hasta que se analiza, las comparaciones de cadenas (como en doctests) aún pueden requerir orden. Entonces no diría que nunca importa.
Michael Scott Cuthbert
1
Si bien eso es cierto para el estándar Javascript (secuencia de comandos ECMA), todas las implementaciones mantienen las claves (cadenas) en orden de origen.
thebjorn
1
@Paulpro realmente? ¿cúal? Sé que Chrome trató de seguir el estándar aquí una vez, pero fue enviado al envío ( code.google.com/p/v8/issues/detail?id=164 ). No pensé que alguien intentaría lo mismo después de eso ...
thebjorn
2
@paulpro estás respondiendo correctamente la pregunta del OP. Sin embargo, quiero agregar que existen usos legítimos para preservar el orden. Por ejemplo, uno puede escribir un script que lea JSON, aplique alguna transformación y reescriba los resultados. Desea preservar el orden para que una herramienta diff muestre claramente los cambios.
Paul Rademacher