Elemente im JSON-Objekt sind mit "json.dumps" nicht in Ordnung.

156

Ich verwende json.dumps, um in json wie umzuwandeln

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

Das Ergebnis, das ich habe, ist:

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

Ich möchte die Schlüssel in der folgenden Reihenfolge haben: ID, Name, Zeitzone - aber stattdessen habe ich Zeitzone, ID, Name.

Wie soll ich das beheben?

Noor
quelle

Antworten:

243

Sowohl Python dict(vor Python 3.7) als auch das JSON-Objekt sind ungeordnete Sammlungen. Sie können sort_keysParameter übergeben, um die Schlüssel zu sortieren:

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

Wenn Sie eine bestimmte Bestellung benötigen; Sie könnten verwendencollections.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}'

Seit Python 3.6 bleibt die Reihenfolge der Schlüsselwortargumente erhalten und die oben genannten können mit einer besseren Syntax neu geschrieben werden:

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

Siehe PEP 468 - Beibehalten der Reihenfolge der Schlüsselwortargumente .

Wenn Ihre Eingabe als JSON angegeben wird, können Sie, um die Reihenfolge beizubehalten (zu erhalten OrderedDict) object_pair_hook, wie von @Fred Yankowski vorgeschlagen, übergeben :

>>> 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
quelle
2
OrderedDict's Init wirklich hässlich
Jean
3
@jean: Der Anfangswert hat nichts damit zu tun OrderedDict(), Sie können ein dictan übergeben OrderedDict(), Sie können auch eine Liste der geordneten Paare an übergeben dict()- obwohl die Reihenfolge in beiden Fällen verloren geht.
JFS
Ich meine, init es, wenn die Reihenfolge beibehalten wird, muss viele '(' und ')'
eingegeben werden
@ Jean: Es gibt ordereddict_literalsaus codetransformerPaket (Alpha-Qualität)
JFS
25
Wenn Sie JSON mit laden d = json.load(f, object_pairs_hook=OrderedDict), json.dump(d)behält ein späterer die Reihenfolge der ursprünglichen Elemente bei.
Fred Yankowski
21

Wie andere erwähnt haben, ist das zugrunde liegende Diktat ungeordnet. Es gibt jedoch OrderedDict-Objekte in Python. (Sie sind in den neuesten Pythons integriert, oder Sie können Folgendes verwenden: http://code.activestate.com/recipes/576693/ ).

Ich glaube, dass neuere Python-JSON-Implementierungen die eingebauten OrderedDicts korrekt verarbeiten, bin mir aber nicht sicher (und ich habe keinen einfachen Zugriff auf Tests).

Alte Python-SimpleJson-Implementierungen verarbeiten die OrderedDict-Objekte nicht gut. Sie konvertieren sie vor der Ausgabe in reguläre Diktate. Sie können dies jedoch wie folgt überwinden:

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)

Wenn wir dies jetzt verwenden, erhalten wir:

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

Welches ist so ziemlich wie gewünscht.

Eine andere Alternative wäre, den Encoder so zu spezialisieren, dass er Ihre Zeilenklasse direkt verwendet, und dann würden Sie kein Zwischendiktat oder UnorderedDict benötigen.

Michael Anderson
quelle
5
Beachten Sie, dass JSON-Objekte immer noch ungeordnet sind . Ein JSON-Client kann die Objektdefinition lesen, die Reihenfolge der Schlüssel vollständig ignorieren und vollständig RFC-kompatibel sein.
Martijn Pieters
4
Martijn ist richtig, dies hat keine Auswirkungen auf die RFC-Konformität, kann aber dennoch wertvoll sein, wenn Sie ein konsistentes Format für Ihren JSON haben möchten (z. B. wenn die Datei unter Versionskontrolle steht oder es einem menschlichen Leser leichter fällt, dies zu tun) verstehen, dass die Reihenfolge der Einträge mit Ihrer Dokumentation übereinstimmt.)
Michael Anderson
3
In diesem Fall setzen Sie einfach auf sort_keys, Truewenn Sie anrufen json.dumps(); Für die Bestellstabilität (zum Testen, stabiles Caching oder VCS-Commits) reichen Sortierschlüssel aus.
Martijn Pieters
7

Die Reihenfolge eines Wörterbuchs hat keine Beziehung zu der Reihenfolge, in der es definiert wurde. Dies gilt für alle Wörterbücher, nicht nur für diejenigen, die in JSON umgewandelt wurden.

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

In der Tat wurde das Wörterbuch "auf den Kopf gestellt", bevor es überhaupt erreichte json.dumps:

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

Hey, ich weiß, es ist so spät für diese Antwort, aber füge sort_keys hinzu und weise ihr wie folgt false zu:

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

das hat bei mir funktioniert

Rabiea Ez Eldeen
quelle
4

json.dump () behält die Reihenfolge Ihres Wörterbuchs bei. Öffnen Sie die Datei in einem Texteditor und Sie werden sehen. Die Bestellung bleibt erhalten, unabhängig davon, ob Sie ihr ein OrderedDict senden.

Json.load () verliert jedoch die Reihenfolge des gespeicherten Objekts, es sei denn, Sie weisen es an, es in ein OrderedDict () zu laden. Dies erfolgt mit dem Parameter object_pairs_hook, wie oben von JFSebastian angegeben.

Andernfalls würde die Reihenfolge verloren gehen, da bei normalem Betrieb das gespeicherte Wörterbuchobjekt in ein reguläres Diktat geladen wird und ein reguläres Diktat die Reihenfolge der angegebenen Elemente nicht beibehält.

Markierung
quelle
Dies ist tatsächlich eine bessere Lösung, da die Aufrechterhaltung der Reihenfolge beim Laden die Bestellung der Speicherauszugszeit übernimmt. Danke für diese Antwort.
Arun R
2

In JSON ist die Reihenfolge der Objektschlüssel wie in Javascript bedeutungslos. Es spielt also keine Rolle, in welcher Reihenfolge sie angezeigt werden. Es handelt sich um dasselbe Objekt.

Paul
quelle
(und das gleiche gilt auch für ein Standard-Python dict)
12
Da JSON jedoch eine Zeichenfolgendarstellung ist, bis es analysiert wird, erfordern Zeichenfolgenvergleiche (wie in Doctests) möglicherweise noch eine Reihenfolge. Also würde ich nicht sagen, dass es nie wichtig ist.
Michael Scott Cuthbert
1
Während dies für den Javascript-Standard (ECMA-Skript) gilt, halten alle Implementierungen (Zeichenfolgen-) Schlüssel in der Quellreihenfolge.
Thebjorn
1
@ Paulpro wirklich? welcher? Ich weiß, dass Chrome hier einmal versucht hat, dem Standard zu folgen, wurde jedoch zur Einreichung gezwungen ( code.google.com/p/v8/issues/detail?id=164 ). Ich hätte nicht gedacht, dass irgendjemand danach das Gleiche versuchen würde ...
thebjorn
2
@paulpro Sie sprechen die Frage des OP richtig an. Ich möchte jedoch hinzufügen, dass es legitime Verwendungszwecke für die Aufrechterhaltung der Ordnung gibt. Beispielsweise kann man ein Skript schreiben, das JSON liest, eine Transformation anwendet und die Ergebnisse zurückschreibt. Sie möchten, dass die Reihenfolge beibehalten wird, damit ein Diff-Tool die Änderungen klar anzeigt.
Paul Rademacher