Elementy w obiekcie JSON nie działają przy użyciu „json.dumps”?

156

Używam json.dumpsdo konwersji na json jak

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

Wynik jaki mam to:

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

Chcę mieć klucze w następującej kolejności: identyfikator, nazwa, strefa czasowa - ale zamiast tego mam strefę czasową, identyfikator, nazwę.

Jak mam to naprawić?

Noor
źródło

Odpowiedzi:

243

Zarówno Python dict(przed Pythonem 3.7), jak i obiekt JSON są kolekcjami nieuporządkowanymi. Możesz podać sort_keysparametr, aby posortować klucze:

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

Jeśli potrzebujesz konkretnego zamówienia; możesz użyćcollections.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}'

Od Pythona 3.6 kolejność argumentów słów kluczowych jest zachowana, a powyższe można przepisać przy użyciu ładniejszej składni:

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

Patrz PEP 468 - Zachowywanie kolejności argumentów słów kluczowych .

Jeśli twoje dane wejściowe są podane jako JSON, a następnie, aby zachować kolejność (do pobrania OrderedDict), możesz przejść object_pair_hook, zgodnie z sugestią @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
źródło
2
Początek OrderedDict jest naprawdę brzydki
jean
3
@jean: wartość początkowa nie ma z tym nic wspólnego OrderedDict(), możesz przekazać dictdo OrderedDict(), możesz również przekazać listę uporządkowanych par dict()- chociaż kolejność jest tracona w obu tych przypadkach.
jfs
Znaczy Init kiedy zachować porządek, musi wpisywać wiele „(” i „)”
Jean
@jean: jest ordereddict_literalsz codetransformerpakietu (jakość alfa)
jfs
25
Ponadto, jeśli załadujesz JSON przy użyciu d = json.load(f, object_pairs_hook=OrderedDict), później json.dump(d)zachowa kolejność oryginalnych elementów.
Fred Yankowski
21

Jak wspominali inni, ta podstawowa zasada jest nieuporządkowana. Jednak w Pythonie istnieją obiekty OrderedDict. (Są wbudowane w najnowsze Pythony lub możesz użyć tego: http://code.activestate.com/recipes/576693/ ).

Uważam, że nowsze implementacje json w Pythonie poprawnie obsługują wbudowane w OrderedDicts, ale nie jestem pewien (i nie mam łatwego dostępu do testów).

Stare implementacje Pythona, simpleejson, nie obsługują ładnie obiektów OrderedDict ... i konwertują je na zwykłe dykty przed ich wyświetleniem ... ale możesz to obejść, wykonując następujące czynności:

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)

teraz używając tego otrzymujemy:

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

Co jest prawie zgodne z oczekiwaniami.

Inną alternatywą byłoby wyspecjalizowanie kodera do bezpośredniego używania twojej klasy wiersza, a wtedy nie potrzebujesz żadnego pośredniego dyktu lub UnorderedDict.

Michael Anderson
źródło
5
Zwróć uwagę, że obiekty JSON są nadal nieuporządkowane ; klient JSON może odczytać definicję obiektu i całkowicie zignorować kolejność kluczy i być w pełni zgodny z RFC.
Martijn Pieters
4
Martijn ma rację, nie wpływa to na zgodność z RFC, ale z pewnością może być cenne, jeśli chcesz mieć spójny format dla swojego JSON (na przykład jeśli plik jest pod kontrolą wersji lub aby ułatwić czytelnikowi zrozumieć, aby zlecenie wejścia było zgodne z twoją dokumentacją.)
Michael Anderson
3
W takim przypadku wystarczy ustawić sort_keyssię Truepodczas rozmowy json.dumps(); dla stabilności zamówienia (do testowania, stabilnego buforowania lub zatwierdzeń VCS) wystarczy sortowanie kluczy.
Martijn Pieters
7

Kolejność słownika nie ma żadnego związku z kolejnością, w jakiej został zdefiniowany. Dotyczy to wszystkich słowników, nie tylko tych, które zostały zamienione na JSON.

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

Rzeczywiście, słownik został przewrócony „do góry nogami”, zanim jeszcze dotarł json.dumps:

>>> {"id":1,"name":"David","timezone":3}
{'timezone': 3, 'id': 1, 'name': 'David'}
David Robinson
źródło
6

hej, wiem, że jest już za późno na tę odpowiedź, ale dodaj sort_keys i przypisz do niej false w następujący sposób:

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

to działało dla mnie

Rabiea Ez Eldeen
źródło
4

json.dump () zachowa porządek w Twoim słowniku. Otwórz plik w edytorze tekstu, a zobaczysz. Zachowa porządek niezależnie od tego, czy wyślesz mu OrderedDict.

Ale json.load () utraci kolejność zapisanego obiektu, chyba że powiesz mu, aby załadował się do OrderedDict (), co jest wykonywane za pomocą parametru object_pairs_hook zgodnie z instrukcją JFSebastian powyżej.

W przeciwnym razie straciłby zamówienie, ponieważ podczas zwykłej operacji ładuje zapisany obiekt słownika do zwykłego dyktu, a zwykły dykt nie zachowuje oderu pozycji, które otrzymał.

oznakowanie
źródło
W rzeczywistości jest to lepsze rozwiązanie, ponieważ utrzymywanie kolejności przy ładowaniu dba o czas składowania. Dzięki za tę odpowiedź.
Arun R
2

w JSON, podobnie jak w JavaScript, kolejność kluczy obiektów jest bez znaczenia, więc tak naprawdę nie ma znaczenia, w jakiej kolejności są wyświetlane, jest to ten sam obiekt.

Paweł
źródło
(i to samo dotyczy standardowego Pythona dict)
12
ale ponieważ JSON jest reprezentacją ciągową do czasu jej przeanalizowania, porównania ciągów (na przykład w testach dokumentów) mogą nadal wymagać kolejności. Więc nie powiedziałbym, że to nigdy nie ma znaczenia.
Michael Scott Cuthbert
1
Chociaż jest to prawdą w przypadku standardu JavaScript (skrypt ECMA), wszystkie implementacje utrzymują klucze (łańcuchowe) w kolejności źródłowej.
thebjorn
1
@Paulpro naprawdę? Który? Wiem, że Chrome próbował kiedyś postępować zgodnie ze standardem tutaj, ale został zmuszony do przesłania ( code.google.com/p/v8/issues/detail?id=164 ). Nie sądziłem, że ktokolwiek spróbuje potem tego samego ...
thebjorn,
2
@paulpro, poprawnie odpowiadasz na pytanie OP. Chcę jednak dodać, że istnieją uzasadnione zastosowania do zachowania porządku. Na przykład można napisać skrypt, który czyta JSON, stosuje jakąś transformację i zapisuje wyniki. Chciałbyś zachować porządek, aby narzędzie porównywania wyraźnie pokazywało zmiany.
Paul Rademacher,