Item dalam objek JSON rusak menggunakan "json.dumps"?

157

Saya menggunakan json.dumpsuntuk mengkonversi menjadi seperti json

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

Hasil yang saya miliki adalah:

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

Saya ingin memiliki kunci dalam urutan berikut: id, nama, zona waktu - tetapi sebaliknya saya memiliki zona waktu, id, nama.

Bagaimana saya memperbaikinya?

Noor
sumber

Jawaban:

244

dictObjek Python (sebelum Python 3.7) dan JSON adalah koleksi tidak berurutan. Anda dapat melewati sort_keysparameter, untuk mengurutkan kunci:

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

Jika Anda membutuhkan pesanan tertentu; Anda bisa menggunakancollections.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}'

Sejak Python 3.6 , urutan argumen kata kunci dipertahankan dan di atas dapat ditulis ulang menggunakan sintaks yang lebih bagus:

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

Lihat PEP 468 - Memelihara Urutan Argumen Kata Kunci .

Jika input Anda diberikan sebagai JSON kemudian untuk mempertahankan pesanan (untuk mendapatkan OrderedDict), Anda bisa lulus object_pair_hook, seperti yang disarankan oleh @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
sumber
2
Init OrderedDict benar-benar jelek
jean
3
@ jean: nilai awal tidak ada sangkut pautnya dengan OrderedDict(), Anda dapat meneruskan dictke OrderedDict(), Anda juga dapat mengirimkan daftar pasangan yang dipesan dict()- meskipun urutannya hilang dalam kedua kasus ini.
jfs
Maksud saya init ketika mempertahankan pesanan, perlu mengetik banyak '(' dan ')'
jean
@ jean: ada ordereddict_literalsdari codetransformerpaket (kualitas alpha)
jfs
25
Juga, jika Anda memuat JSON menggunakan d = json.load(f, object_pairs_hook=OrderedDict), nanti json.dump(d)akan mempertahankan urutan elemen asli.
Fred Yankowski
21

Seperti yang disebutkan orang lain, mendikte yang mendasari tidak terurut. Namun ada objek OrderedDict di python. (Mereka dibangun di dalam ular sanca baru-baru ini, atau Anda dapat menggunakan ini: http://code.activestate.com/recipes/576693/ ).

Saya percaya bahwa implementasi python json yang lebih baru dengan benar menangani dibangun di OrderedDicts, tapi saya tidak yakin (dan saya tidak memiliki akses mudah untuk menguji).

Implementasi pyjons simplejson lama tidak menangani objek OrderedDict dengan baik .. dan mengonversinya menjadi dicts biasa sebelum mengeluarkannya .. tetapi Anda dapat mengatasinya dengan melakukan hal berikut:

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)

sekarang menggunakan ini kita dapatkan:

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

Cukup banyak yang diinginkan.

Alternatif lain adalah mengkhususkan pembuat enkode untuk secara langsung menggunakan kelas baris Anda, dan kemudian Anda tidak akan memerlukan dict menengah atau UnorderedDict.

Michael Anderson
sumber
5
Perhatikan bahwa objek JSON masih berupa unordered ; klien JSON dapat membaca definisi objek dan sepenuhnya mengabaikan urutan kunci dan sepenuhnya sesuai dengan RFC.
Martijn Pieters
4
Martijn benar, ini tidak mempengaruhi kepatuhan RFC, tetapi tentu saja masih bisa bernilai jika Anda ingin memiliki format yang konsisten untuk JSON Anda (Misalnya jika file tersebut di bawah kontrol versi, atau untuk memudahkan pembaca manusia untuk pahami, untuk membuat entri entri sesuai dengan dokumentasi Anda.)
Michael Anderson
3
Dalam hal ini Anda hanya mengatur sort_keysuntuk Truesaat memanggiljson.dumps() ; untuk stabilitas pesanan (untuk pengujian, caching stabil atau komitmen VCS), kunci penyortiran sudah cukup.
Martijn Pieters
7

Urutan kamus tidak memiliki hubungan dengan urutan yang didefinisikan. Ini berlaku untuk semua kamus, bukan hanya kamus yang diubah menjadi JSON.

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

Memang, kamus itu "terbalik" sebelum mencapai json.dumps:

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

hei saya tahu sudah sangat terlambat untuk jawaban ini tetapi tambahkan sort_keys dan tetapkan false untuk itu sebagai berikut:

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

ini bekerja untuk saya

Rabiea Ez Eldeen
sumber
4

json.dump () akan mempertahankan ordder kamus Anda. Buka file dalam editor teks dan Anda akan melihat. Itu akan mempertahankan pesanan terlepas dari apakah Anda mengirimnya suatu Pesanan atau tidak.

Tetapi json.load () akan kehilangan urutan objek yang disimpan kecuali Anda mengatakannya untuk memuat ke dalam OrderedDict (), yang dilakukan dengan parameter object_pairs_hook seperti yang diinstruksikan JFSebastian di atas.

Jika tidak maka akan kehilangan urutan karena dalam operasi biasa, itu memuat objek kamus yang disimpan ke dalam dikt reguler dan dikt reguler tidak mempertahankan oder item yang diberikan.

markling
sumber
Ini sebenarnya adalah perbaikan yang lebih baik karena menjaga urutan pada muatan menangani waktu dump pemesanan. Terima kasih atas jawaban ini.
Arun R
2

di JSON, seperti pada Javascript, urutan kunci objek tidak ada artinya, jadi tidak masalah urutan apa yang ditampilkan, itu adalah objek yang sama.

Paul
sumber
(dan hal yang sama juga berlaku untuk Python standar dict)
12
tetapi karena JSON adalah representasi string hingga diuraikan, perbandingan string (seperti dalam dokumen) mungkin masih memerlukan pesanan. Jadi saya tidak akan mengatakan itu tidak masalah.
Michael Scott Cuthbert
1
Sementara itu berlaku untuk standar Javascript (skrip ECMA), semua implementasi menyimpan kunci (string) dalam urutan sumber.
thebjorn
1
@ Paulpro benar-benar? yang mana? Saya tahu Chrome mencoba mengikuti standar di sini satu kali, tetapi dilemparkan ke dalam pengiriman ( code.google.com/p/v8/issues/detail?id=164 ). Saya tidak berpikir ada orang yang akan mencoba hal yang sama setelah itu ...
thebjorn
2
@paulpro Anda telah menjawab pertanyaan OP dengan benar. Saya ingin menambahkan, bahwa ada kegunaan yang sah untuk menjaga ketertiban. Misalnya, seseorang dapat menulis skrip yang bertuliskan JSON, menerapkan beberapa transformasi, dan menulis kembali hasilnya. Anda ingin pesanan dipertahankan agar alat berbeda akan dengan jelas menunjukkan perubahan.
Paul Rademacher