web-dev-qa-db-fra.com

Puis-je charger JSON dans un OrderedDict?

Ok, je peux donc utiliser un OrderedDict dans json.dump. C'est-à-dire qu'un OrderedDict peut être utilisé comme une entrée pour JSON.

Mais peut-il être utilisé comme une sortie? Si c'est le cas, comment? Dans mon cas, je voudrais load dans un OrderedDict afin de pouvoir conserver l'ordre des clés dans le fichier.

Si non, existe-t-il une solution de contournement?

405
c00kiemonster

Oui, vous pouvez. En spécifiant l'argument object_pairs_hook à JSONDecoder . En fait, ceci est l'exemple exact donné dans la documentation.

>>> json.JSONDecoder(object_pairs_hook=collections.OrderedDict).decode('{"foo":1, "bar": 2}')
OrderedDict([('foo', 1), ('bar', 2)])
>>> 

Vous pouvez transmettre ce paramètre à json.loads (si vous n'avez pas besoin d'une instance de Decoder à d'autres fins) comme ceci:

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

Utiliser json.load se fait de la même manière:

>>> data = json.load(open('config.json'), object_pairs_hook=OrderedDict)
572

Version simple pour Python 2.7+

my_ordered_dict = json.loads(json_str, object_pairs_hook=collections.OrderedDict)

Ou pour Python 2.4 à 2.6

import simplejson as json
import ordereddict

my_ordered_dict = json.loads(json_str, object_pairs_hook=ordereddict.OrderedDict)
124
mjhm

Quelques bonnes nouvelles! Depuis la version 3.6, l’implémentation cPython a conservé l’ordre d’insertion des dictionnaires ( https://mail.python.org/pipermail/python-dev/2016-September/146327.html ). Cela signifie que la bibliothèque json est maintenant en ordre conservant par défaut. Observez la différence de comportement entre python 3.5 et 3.6. Le code:

import json
data = json.loads('{"foo":1, "bar":2, "fiddle":{"bar":2, "foo":1}}')
print(json.dumps(data, indent=4))

Dans py3.5, l'ordre résultant est indéfini:

{
    "fiddle": {
        "bar": 2,
        "foo": 1
    },
    "bar": 2,
    "foo": 1
}

Dans l'implémentation cPython de python 3.6:

{
    "foo": 1,
    "bar": 2,
    "fiddle": {
        "bar": 2,
        "foo": 1
    }
}

La très bonne nouvelle est que cela est devenu une spécification de langage à partir de python 3.7 (par opposition à un détail d'implémentation de cPython 3.6+): https://mail.python.org/pipermail /python-dev/2017-december/151283.html

La réponse à votre question devient alors la suivante: effectuez une mise à niveau vers python 3.6! :)

29
pelson

Vous pouvez toujours écrire la liste des clés en plus de décharger le dict, puis reconstruire la OrderedDict en parcourant la liste?

7
Amber

Outre le vidage de la liste de clés ordonnée à côté du dictionnaire, une autre solution peu évoluée, qui présente l'avantage d'être explicite, consiste à vider la liste (ordonnée) de paires clé-valeur ordered_dict.items(); le chargement est une simple OrderedDict(<list of key-value pairs>). Cela gère un dictionnaire commandé malgré le fait que JSON n’a pas ce concept (les dictionnaires JSON n’ont pas d’ordre).

Il est en effet agréable de tirer parti du fait que json vide le OrderedDict dans le bon ordre. Cependant, il est en général inutilement lourd et pas nécessairement significatif de devoir lire tous dictionnaires JSON en tant que OrderedDict (via l'argument object_pairs_hook), donc une conversion explicite de niquement les dictionnaires à commander ont également un sens.

5
Eric O Lebigot

La commande load normalement utilisée fonctionnera si vous spécifiez le paramètre 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)
3
ntg