Saya memiliki Python set
yang berisi objek __hash__
dan __eq__
metode untuk memastikan tidak ada duplikat yang disertakan dalam koleksi.
Saya perlu json menyandikan hasil ini set
, tetapi melewati bahkan kosong set
ke json.dumps
metode menimbulkan a TypeError
.
File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python2.7/json/encoder.py", line 178, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: set([]) is not JSON serializable
Saya tahu saya dapat membuat ekstensi ke json.JSONEncoder
kelas yang memiliki default
metode khusus , tapi saya bahkan tidak yakin harus mulai dari mana untuk mengkonversi set
. Haruskah saya membuat kamus dari set
nilai - nilai dalam metode default, dan kemudian mengembalikan pengkodean itu? Idealnya, saya ingin membuat metode default dapat menangani semua tipe data yang disandikan oleh pembuat kode asli (Saya menggunakan Mongo sebagai sumber data sehingga tanggal tampaknya juga meningkatkan kesalahan ini)
Setiap petunjuk ke arah yang benar akan dihargai.
EDIT:
Terima kasih atas jawabannya! Mungkin saya seharusnya lebih tepat.
Saya menggunakan (dan meningkatkan) jawaban di sini untuk mengatasi keterbatasan set
terjemahan, tetapi ada kunci internal yang menjadi masalah juga.
Objek dalam objek set
kompleks yang diterjemahkan __dict__
, tetapi mereka sendiri juga dapat berisi nilai untuk properti mereka yang bisa tidak memenuhi syarat untuk tipe dasar dalam json encoder.
Ada banyak jenis berbeda yang masuk ke dalam ini set
, dan hash pada dasarnya menghitung id unik untuk entitas, tetapi dalam semangat sejati NoSQL tidak ada yang tahu persis apa isi objek anak.
Satu objek mungkin berisi nilai tanggal untuk starts
, sedangkan yang lain mungkin memiliki beberapa skema lain yang tidak menyertakan kunci yang berisi objek "non-primitif".
Itulah sebabnya satu-satunya solusi yang dapat saya pikirkan adalah memperpanjang JSONEncoder
untuk mengganti default
metode untuk menghidupkan kasus yang berbeda - tapi saya tidak yakin bagaimana cara melakukannya dan dokumentasi ini ambigu. Dalam objek bersarang, apakah nilai yang dikembalikan dari default
pergi dengan kunci, atau hanya menyertakan / membuang umum yang melihat seluruh objek? Bagaimana metode itu mengakomodasi nilai bersarang? Saya telah memeriksa pertanyaan-pertanyaan sebelumnya dan sepertinya tidak dapat menemukan pendekatan terbaik untuk pengkodean spesifik kasus (yang sayangnya sepertinya perlu saya lakukan di sini).
sumber
dict
? Saya pikir Anda ingin membuat hanyalist
dari set dan kemudian meneruskannya ke encoder ... misalnya:encode(list(myset))
Jawaban:
Notasi JSON hanya memiliki segelintir tipe data asli (objek, array, string, angka, boolean, dan null), jadi apa pun yang diserialisasi dalam JSON perlu dinyatakan sebagai salah satu dari tipe ini.
Seperti yang ditunjukkan dalam modul modul json , konversi ini dapat dilakukan secara otomatis oleh JSONEncoder dan JSONDecoder , tetapi kemudian Anda akan menyerahkan beberapa struktur lain yang mungkin Anda perlukan (jika Anda mengonversi set ke daftar, maka Anda kehilangan kemampuan untuk memulihkan secara teratur daftar; jika Anda mengonversi set ke kamus menggunakan
dict.fromkeys(s)
maka Anda kehilangan kemampuan untuk memulihkan kamus).Solusi yang lebih canggih adalah membangun tipe kustom yang dapat hidup berdampingan dengan tipe JSON asli lainnya. Ini memungkinkan Anda menyimpan struktur bersarang yang mencakup daftar, set, dicts, desimal, objek datetime, dll .:
Berikut adalah contoh sesi yang menunjukkan bahwa ia dapat menangani daftar, dikte, dan set:
Sebagai alternatif, mungkin berguna untuk menggunakan teknik serialisasi yang lebih umum seperti YAML , Twisted Jelly , atau modul acar Python . Masing-masing mendukung berbagai tipe data yang jauh lebih besar.
sumber
JSONDecoder
tetapi tidak menggunakannyaAnda dapat membuat pembuat enkode khusus yang mengembalikan a
list
ketika bertemu aset
. Ini sebuah contoh:Anda dapat mendeteksi tipe lain dengan cara ini juga. Jika Anda perlu mempertahankan bahwa daftar itu sebenarnya adalah set, Anda dapat menggunakan penyandian khusus. Sesuatu seperti
return {'type':'set', 'list':list(obj)}
mungkin bekerja.Untuk ilustrasi tipe bertingkat, pertimbangkan untuk membuat serial ini:
Ini memunculkan kesalahan berikut:
Ini menunjukkan bahwa pembuat enkode akan
list
mengembalikan hasilnya dan secara berulang memanggil serializer pada anak-anaknya. Untuk menambahkan serializer khusus untuk banyak jenis, Anda dapat melakukan ini:sumber
default
fungsi tersebut akan dipanggil lagi, kali ini denganobj
menjadi objek tanggal, jadi Anda hanya perlu mengujinya dan mengembalikan representasi tanggal.Saya mengadaptasi solusi Raymond Hettinger ke python 3.
Inilah yang telah berubah:
unicode
lenyapdefault
dengansuper()
base64
untuk membuat cerita bersambungbytes
jenis menjadistr
(karena tampaknyabytes
dalam python 3 tidak dapat dikonversi ke JSON)sumber
json.dumps()
kembali ke / dari'latin1'
, melewatkanbase64
hal - hal yang tidak perlu.Hanya kamus, Daftar, dan tipe objek primitif (int, string, bool) yang tersedia di JSON.
sumber
Anda tidak perlu membuat kelas pembuat enkode khusus untuk memasok
default
metode - metode ini dapat dilewatkan sebagai argumen kata kunci:menghasilkan
[1, 2, 3]
semua versi Python yang didukung.sumber
Jika Anda hanya perlu menyandikan set, bukan objek Python umum, dan ingin membuatnya mudah dibaca oleh manusia, versi sederhana jawaban Raymond Hettinger dapat digunakan:
sumber
Jika Anda hanya perlu dump cepat dan tidak ingin mengimplementasikan encoder kustom. Anda dapat menggunakan yang berikut ini:
json_string = json.dumps(data, iterable_as_array=True)
Ini akan mengubah semua set (dan iterables lainnya) menjadi array. Berhati-hatilah karena bidang-bidang itu akan tetap array ketika Anda mengurai json kembali. Jika Anda ingin mempertahankan jenisnya, Anda perlu menulis pembuat enkode khusus.
sumber
Salah satu kekurangan dari solusi yang diterima adalah bahwa outputnya sangat spesifik python. Yaitu output mentah json tidak dapat diamati oleh manusia atau diambil oleh bahasa lain (misalnya javascript). contoh:
Akan membuat Anda:
Saya dapat mengusulkan solusi yang menurunkan set ke dikt yang berisi daftar di jalan keluar, dan kembali ke set ketika dimuat ke python menggunakan encoder yang sama, karena itu menjaga agnostisisme pengamatan dan bahasa:
Yang membuat Anda:
Perhatikan bahwa membuat serialisasi kamus yang memiliki elemen dengan kunci
"__set__"
akan merusak mekanisme ini. Jadi__set__
sekarang telah menjadidict
kunci yang dipesan . Jelas merasa bebas untuk menggunakan kunci lain yang lebih dalam dan dikaburkan.sumber