python - ¿Puedo hacer que JSON se cargue en un OrderedDict?
load ordereddictionary (6)
Ok, así que puedo usar un OrderedDict en json.dump
. Es decir, un OrderedDict se puede usar como entrada para JSON.
¿Pero puede ser usado como una salida? ¿Si es así, cómo? En mi caso, me gustaría load
en OrderedDict para poder mantener el orden de las claves en el archivo.
Si no, ¿hay algún tipo de solución?
¿Siempre podría escribir la lista de claves además de descargar el dictado y luego reconstruir OrderedDict
iterando a través de la lista?
Además de volcar la lista ordenada de claves junto con el diccionario, otra solución de baja tecnología, que tiene la ventaja de ser explícita, es volcar la lista (ordenada) de pares clave-valor ordered_dict.items()
; La carga es un simple OrderedDict(<list of key-value pairs>)
. Esto maneja un diccionario ordenado a pesar del hecho de que JSON no tiene este concepto (los diccionarios JSON no tienen orden).
De hecho, es bueno aprovechar el hecho de que json
vuelca el OrderedDict en el orden correcto. Sin embargo, en general es innecesariamente pesado y no necesariamente significativo tener que leer todos los diccionarios JSON como OrderedDict (a través del argumento object_pairs_hook
), por lo que una conversión explícita de solo los diccionarios que deben ordenarse también tiene sentido.
Algunas buenas noticias! Desde la versión 3.6, la implementación de cPython ha preservado el orden de inserción de los diccionarios ( https://mail.python.org/pipermail/python-dev/2016-September/146327.html ). Esto significa que la biblioteca json ahora está conservando el orden de forma predeterminada. Observe la diferencia de comportamiento entre Python 3.5 y 3.6. El código:
import json
data = json.loads(''{"foo":1, "bar":2, "fiddle":{"bar":2, "foo":1}}'')
print(json.dumps(data, indent=4))
En py3.5 el orden resultante es indefinido:
{
"fiddle": {
"bar": 2,
"foo": 1
},
"bar": 2,
"foo": 1
}
En la implementación cPython de python 3.6:
{
"foo": 1,
"bar": 2,
"fiddle": {
"bar": 2,
"foo": 1
}
}
La gran noticia es que se ha convertido en una especificación de lenguaje a partir de python 3.7 (a diferencia de un detalle de implementación de cPython 3.6+): https://mail.python.org/pipermail/python-dev/2017-December/151283.html
Entonces, la respuesta a tu pregunta ahora es: ¡actualiza a Python 3.6! :)
El comando de carga normalmente utilizado funcionará si especifica el parámetro object_pairs_hook :
import json
from collections import OrderedDict
with open(''foo.json'', ''r'') as fp:
metrics_types = json.load(fp, object_pairs_hook=OrderedDict)
Sí tu puedes. Al especificar el argumento JSONDecoder en JSONDecoder . De hecho, este es el ejemplo exacto dado en la documentación.
>>> json.JSONDecoder(object_pairs_hook=collections.OrderedDict).decode(''{"foo":1, "bar": 2}'')
OrderedDict([(''foo'', 1), (''bar'', 2)])
>>>
Puede pasar este parámetro a json.loads
(si no necesita una instancia de Decoder para otros propósitos) así:
>>> import json
>>> from collections import OrderedDict
>>> data = json.loads(''{"foo":1, "bar": 2}'', object_pairs_hook=OrderedDict)
>>> print json.dumps(data, indent=4)
{
"foo": 1,
"bar": 2
}
>>>
Usar json.load
se hace de la misma manera:
>>> data = json.load(open(''config.json''), object_pairs_hook=OrderedDict)
Versión simple para Python 2.7+
my_ordered_dict = json.loads(json_str, object_pairs_hook=collections.OrderedDict)
O para Python 2.4 a 2.6
import simplejson as json
import ordereddict
my_ordered_dict = json.loads(json_str, object_pairs_hook=ordereddict.OrderedDict)