Posso caricare JSON in un OrderedDict?


428

Ok, quindi posso usare un OrderedDict in json.dump . Cioè, un OrderedDict può essere utilizzato come input per JSON.

Ma può essere usato come output? Se é cosi, come? Nel mio caso, vorrei loadinserire un OrderedDict in modo da poter mantenere l'ordine delle chiavi nel file.

In caso contrario, esiste una sorta di soluzione alternativa?


Non ho mai provato a mantenere l'ordine, anche se posso certamente vedere come sarebbe utile.
feathj,

1
Sì, nel mio caso sto colmando il divario tra diverse lingue e applicazioni e JSON funziona molto bene. Ma l'ordinamento delle chiavi è un po 'un problema. Sarebbe fantastico avere un semplice segno di spunta json.loadper usare OrderedDicts invece di Dicts in Python.
c00kiemonster,

3
Le specifiche JSON definiscono il tipo di oggetto con chiavi non ordinate ... aspettarsi un ordine di chiavi specifico è un errore
Anentropic

3
L'ordinamento delle chiavi non è in genere per alcun tipo di requisiti funzionali. È principalmente solo per la leggibilità umana. Se voglio solo che il mio json sia piuttosto stampato, non mi aspetto che l'ordine del documento cambi affatto.
Sottaceti

5
Aiuta anche a evitare le differenze con git!
Richard Rast,

Risposte:


610

Si, puoi. Specificando l' object_pairs_hookargomento su JSONDecoder . In realtà, questo è l'esempio esatto riportato nella documentazione.

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

Puoi passare questo parametro a json.loads(se non hai bisogno di un'istanza Decoder per altri scopi) in questo modo:

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

L'utilizzo json.loadavviene allo stesso modo:

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

3
Sono perplesso. I documenti dicono che object_pairs_hook viene chiamato per ogni letterale che viene decodificato in coppie. Perché questo non crea un nuovo OrderedDict per ogni record in JSON?
Tim Keating

3
Hmm ... i documenti sono in qualche modo definiti in modo ambiguo. Cosa significano che "l'intero risultato della decodifica di tutte le coppie" verrà passato, in ordine, come elenco, a object_pairs_hook, piuttosto che "ogni coppia verrà passata a object_pairs_hook",
SingleNegationElimination

Ma perde l'ordine originale dell'input json?
SIslam,

Sono stato sorpreso di vedere che json.loadnon lo mantiene ordinato per impostazione predefinita, ma sembra che stia solo rispecchiando ciò che fa json stesso - {}sono non ordinati, ma i []nel json sono ordinati come descritto qui
cardamomo

1
@RandomC accertty sì, ogni volta che si incontra un oggetto JSON durante l'analisi della sorgente, OrderedDictverrà utilizzato per costruire il valore python risultante.
SingleNegationElimination

125

Versione semplice per Python 2.7+

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

O per Python da 2.4 a 2.6

import simplejson as json
import ordereddict

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

4
Ahhh, ma non include object_pairs_hook - motivo per cui hai ancora bisogno di simplejson in 2.6. ;)
mjhm,

8
Vuoi nota che simplejsone ordereddictsono librerie separate che è necessario installare.
phunehehe,

2
per python 2.7+: "importa json, collezioni" nel codice, per python2.6- "aptitude install python-pip" e "pip install ordereddict" nel sistema
ZiTAL

Questo è molto più semplice e veloce rispetto al metodo precedente con JSONDecoder.
Natim,

Stranamente, in pypy, il json incluso non ci riuscirà loads('{}', object_pairs_hook=OrderedDict).
Matthew Schinckel,

37

Alcune grandi notizie! Dalla versione 3.6 l'implementazione di cPython ha conservato l'ordine di inserimento dei dizionari ( https://mail.python.org/pipermail/python-dev/2016-September/146327.html ). Ciò significa che la libreria json ora sta conservando l'ordine per impostazione predefinita. Osservare la differenza di comportamento tra Python 3.5 e 3.6. Il codice:

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

In py3.5 l'ordine risultante non è definito:

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

Nell'implementazione di cPython di python 3.6:

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

La notizia davvero fantastica è che questa è diventata una specifica del linguaggio a partire da Python 3.7 (al contrario di un dettaglio di implementazione di cPython 3.6+): https://mail.python.org/pipermail/python-dev/2017-December/151283 .html

Quindi la risposta alla tua domanda ora diventa: aggiorna a Python 3.6! :)


1
Anche se vedo lo stesso comportamento di te nell'esempio fornito, l'implementazione di CPython di Python 3.6.4 json.loads('{"2": 2, "1": 1}')diventa {'1': 1, '2': 2}per me.
Fuglede,

1
@fuglede sembra che dict.__repr__ordina le chiavi mentre l'ordinamento sottostante viene conservato. In altre parole, json.loads('{"2": 2, "1": 1}').items()è dict_items([('2', 2), ('1', 1)])anche se lo repr(json.loads('{"2": 2, "1": 1}'))è "{'1': 1, '2': 2}".
Simon Charette,

@SimonCharette Hm, potrebbe essere; In realtà non sono in grado di riprodurre la mia osservazione in pkgs / main / win-64 di conda / python-3.6.4-h0c2934d_3, quindi sarà difficile testarlo.
Fuglede,

Questo non aiuta molto, dal momento che "rinominare" le chiavi rovinerà comunque l'ordine delle chiavi.
Hubro

7

Potresti sempre scrivere l'elenco delle chiavi oltre a scaricare il dict e quindi ricostruire OrderedDictiterando l'elenco?


1
+1 per soluzione a bassa tecnologia. L'ho fatto quando ho a che fare con lo stesso problema con YAML, ma dover duplicare è un po 'zoppo, specialmente quando il formato sottostante mantiene l'ordine. Potrebbe anche avere senso evitare di perdere le coppie chiave-valore che sono nel dict ma mancano dall'elenco delle chiavi, applicandole dopo tutti gli articoli esplicitamente ordinati.
Mu Mind,

2
La soluzione a bassa tecnologia conserva anche un contesto che altrimenti non è necessariamente conservato nel formato esportato (IOW; qualcuno vede JSON e non c'è nulla che affermi esplicitamente "queste chiavi dovrebbero rimanere in questo ordine" se ci manipolano).
Ambra,

Cosa determina che l'elenco dei tasti "scaricati" sono nell'ordine giusto? Che dire dei dadi annidati? Sembra che sia il dumping dovrebbe gestirlo che la ricostruzione dovrebbe essere fatta in modo ricorsivo usando OrdereDicts.
martineau,

5

Oltre a scaricare l'elenco ordinato di chiavi accanto al dizionario, un'altra soluzione a bassa tecnologia, che ha il vantaggio di essere esplicita, è quella di scaricare l'elenco (ordinato) di coppie chiave-valore ordered_dict.items(); il caricamento è sempliceOrderedDict(<list of key-value pairs>) . Gestisce un dizionario ordinato nonostante JSON non abbia questo concetto (i dizionari JSON non hanno alcun ordine).

È davvero bello approfittare del fatto che jsonscarica OrderedDict nell'ordine corretto. Tuttavia, in generale è inutilmente pesante e non necessariamente significativo leggere tutti i dizionari JSON come un OrderedDict (attraverso l' object_pairs_hookargomento), quindi ha senso anche una conversione esplicita dei soli dizionari che devono essere ordinati.


4

Il comando di caricamento normalmente utilizzato funzionerà se si specifica il parametro 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.