Bagaimana cara membuat kelas Python serializable?
Kelas sederhana:
class FileItem:
def __init__(self, fname):
self.fname = fname
Apa yang harus saya lakukan untuk bisa mendapatkan output:
>>> import json
>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable
Tanpa kesalahan
python
json
serialization
Sergey
sumber
sumber
import jsons
lihat jawaban di bawah - ini berfungsi dengan baikJawaban:
Apakah Anda memiliki gagasan tentang output yang diharapkan? Untuk misalnya apakah ini akan dilakukan?
Dalam hal ini Anda hanya dapat menelepon
json.dumps(f.__dict__)
.Jika Anda ingin lebih banyak output yang disesuaikan, Anda harus subclass
JSONEncoder
dan mengimplementasikan serialisasi kustom Anda sendiri.Untuk contoh sepele, lihat di bawah.
Kemudian Anda meneruskan kelas ini ke
json.dumps()
metode sebagaicls
kwarg:Jika Anda juga ingin memecahkan kode maka Anda harus menyediakan custom
object_hook
keJSONDecoder
kelas. Untuk misalnyasumber
__dict__
tidak akan berfungsi dalam semua kasus. Jika atribut belum disetel setelah objek dibuat,__dict__
mungkin tidak sepenuhnya diisi. Dalam contoh di atas, Anda baik-baik saja, tetapi jika Anda memiliki atribut kelas yang juga ingin Anda enkode, itu tidak akan terdaftar__dict__
kecuali mereka telah dimodifikasi dalam__init__
panggilan kelas atau dengan cara lain setelah objek tersebut dipakai.from_json()
fungsi yang digunakan sebagai objek-kait harus memilikielse: return json_object
pernyataan, sehingga dapat menangani objek umum juga.__dict__
juga tidak berfungsi jika Anda menggunakan__slots__
kelas gaya baru.JSONEncoder
seperti di atas untuk membuat protokol kustom, seperti memeriksa keberadaan__json_serializable__
metode dan memanggilnya untuk mendapatkan representasi JSON serializable objek. Ini akan menjadi sesuai dengan pola Python lainnya, seperti__getitem__
,__str__
,__eq__
, dan__len__
.__dict__
juga tidak akan bekerja secara rekursif, misalnya, jika atribut objek Anda adalah objek lain.Berikut ini adalah solusi sederhana untuk fitur sederhana:
.toJSON()
metodeAlih-alih kelas JSON serializable, terapkan metode serializer:
Jadi Anda cukup menyebutnya untuk membuat serial:
akan menampilkan:
sumber
o.__dict___
. Coba contoh Anda sendiri:class MyObject(): def __init__(self): self.prop = 1 j = json.dumps({ "foo": "bar", "baz": MyObject() }, default=lambda o: o.__dict__)
a.__dict__
/b.__dict__
.datetime.datetime
instance. Itu melempar kesalahan berikut:'datetime.datetime' object has no attribute '__dict__'
Untuk kelas yang lebih kompleks, Anda dapat mempertimbangkan alat jsonpickle :
(tautan ke jsonpickle di PyPi)
sumber
jsonpickle
objek. Juga, ini tidak dapat men-decode dicts dicts yang berisi bingkai data panda.obj = jsonpickle.decode(file.read())
danfile.write(jsonpickle.encode(obj))
.Sebagian besar jawabannya melibatkan mengubah panggilan ke json.dumps () , yang tidak selalu mungkin atau diinginkan (mungkin terjadi di dalam komponen kerangka kerja misalnya).
Jika Anda ingin dapat memanggil json.dumps (obj) apa adanya, maka solusi sederhana diwarisi dari dict :
Ini berfungsi jika kelas Anda hanya representasi data dasar, untuk hal-hal rumit Anda selalu dapat mengatur kunci secara eksplisit.
sumber
dumps
itu bukanlah solusi yang baik. Ngomong-ngomong, dalam kebanyakan kasus Anda mungkin ingin memilikidict
warisan bersama dengan delegasi, yang berarti bahwa Anda akan memiliki beberapadict
tipe atribut di dalam kelas Anda, Anda kemudian akan meneruskan atribut ini sebagai parameter seperti inisialisasisuper().__init__(self.elements)
.Saya suka jawaban Onur tetapi akan diperluas untuk memasukkan
toJSON()
metode opsional untuk objek untuk membuat cerita bersambung sendiri:sumber
json.dumps
penanganan kustom yang ada dan memperkenalkan. Terima kasih!try-catch
mungkin melakukan sesuatu sepertiif 'toJSON' in obj.__attrs__():
... untuk menghindari kegagalan diam (jika terjadi kegagalan toJSON () karena beberapa alasan selain tidak ada di sana) ... kegagalan yang berpotensi menyebabkan korupsi data.Pilihan lain adalah untuk membungkus dumping JSON di kelasnya sendiri:
Atau, bahkan lebih baik lagi, mensubclass kelas FileItem dari suatu
JsonSerializable
kelas:Pengujian:
sumber
__json__encode__
/__json_decode__
(pengungkapan: Saya membuat yang terakhir).Cukup tambahkan
to_json
metode ke kelas Anda seperti ini:Dan tambahkan kode ini (dari jawaban ini ) , ke suatu tempat di atas segalanya:
Ini akan me-monkey-patch modul json ketika diimpor sehingga JSONEncoder.default () secara otomatis memeriksa metode "to_json ()" khusus dan menggunakannya untuk menyandikan objek jika ditemukan.
Seperti yang dikatakan Onur, tetapi kali ini Anda tidak perlu memperbarui setiap
json.dumps()
proyek Anda.sumber
TheObject.to_json = my_serializer
.Saya menemukan masalah ini tempo hari dan menerapkan versi yang lebih umum dari Encoder untuk objek Python yang dapat menangani objek bersarang dan bidang yang diwarisi :
Contoh:
Hasil:
sumber
return obj
di baris terakhir saya melakukan inireturn super(ObjectEncoder, self).default(obj)
. Referensi DI SINIJika Anda menggunakan Python3.5 +, Anda bisa menggunakan
jsons
. Ini akan mengubah objek Anda (dan semua atributnya secara rekursif) menjadi dict.Atau jika Anda menginginkan string:
Atau jika kelas Anda diimplementasikan
jsons.JsonSerializable
:sumber
jsons
perpustakaan dengan dataclasses . Sejauh ini, sangat baik untuk saya!jika menggunakan standar
json
, Anda perlu mendefinisikan suatudefault
fungsisumber
json.dumps(User('alice', '[email protected]'), default=lambda x: x.__dict__)
json
terbatas dalam hal objek yang dapat dicetak, danjsonpickle
(Anda mungkin perlupip install jsonpickle
) terbatas dalam hal tidak dapat membuat teks. Jika Anda ingin memeriksa konten objek yang kelasnya tidak dapat Anda ubah, saya masih tidak bisa menemukan cara yang lebih lurus daripada:Catatan: mereka tetap tidak dapat mencetak metode objek.
sumber
Kelas ini dapat melakukan triknya, mengubah objek menjadi standar json.
pemakaian:
bekerja di
python2.7
danpython3
.sumber
sumber
default(obj)
adalah fungsi yang harus mengembalikan versi obj yang bisa serial atau meningkatkan TypeError. Defaultnyadefault
hanya memunculkan TypeError.jaraco memberikan jawaban yang cukup rapi. Saya perlu memperbaiki beberapa hal kecil, tetapi ini berfungsi:
Kode
Perhatikan bahwa kita perlu dua langkah untuk memuat. Untuk saat ini,
__python__
properti tidak digunakan.Seberapa umum ini?
Dengan menggunakan metode AlJohri , saya memeriksa popularitas pendekatan:
Serialisasi (Python -> JSON):
to_json
: 266.595 pada 2018-06-27toJSON
: 96.307 pada 2018-06-27__json__
: 8.504 pada 2018-06-27for_json
: 6,937 pada 2018-06-27Deserialisasi (JSON -> Python):
from_json
: 226.101 pada 2018-06-27sumber
Ini telah bekerja dengan baik untuk saya:
lalu
dan
sumber
Jika Anda tidak keberatan menginstal paket untuk itu, Anda dapat menggunakan json-trik :
Setelah itu Anda hanya perlu mengimpor
dump(s)
darijson_tricks
bukan json, dan biasanya akan berfungsi:yang akan memberi
Dan pada dasarnya itu!
Ini akan bekerja dengan baik pada umumnya. Ada beberapa pengecualian, misalnya jika terjadi hal-hal khusus
__new__
, atau lebih banyak keajaiban metaclass terjadi.Jelas memuat juga berfungsi (jika tidak apa gunanya):
Ini mengasumsikan yang
module_name.test_class.MyTestCls
dapat diimpor dan tidak berubah dengan cara yang tidak kompatibel. Anda akan mendapatkan kembali sebuah instance , bukan kamus atau sejenisnya, dan itu harus merupakan salinan yang identik dengan yang Anda buang.Jika Anda ingin mengkustomisasi bagaimana sesuatu mendapat (de) serial, Anda dapat menambahkan metode khusus ke kelas Anda, seperti:
yang membuat cerita bersambung hanya sebagian dari parameter atribut, sebagai contoh.
Dan sebagai bonus gratis, Anda mendapatkan (de) serialisasi array numpy, tanggal & waktu, peta yang dipesan, serta kemampuan untuk memasukkan komentar di json.
Penafian: Saya membuat json_tricks , karena saya memiliki masalah yang sama dengan Anda.
sumber
jsonweb tampaknya menjadi solusi terbaik untuk saya. Lihat http://www.jsonweb.info/en/latest/
sumber
Ini adalah 3 sen saya ...
Ini menunjukkan serialisasi json eksplisit untuk objek python seperti pohon.
Catatan: Jika Anda benar-benar menginginkan beberapa kode seperti ini, Anda bisa menggunakan kelas FilePath bengkok .
sumber
Saya mengalami masalah ini ketika saya mencoba menyimpan model Peewee ke dalam PostgreSQL
JSONField
.Setelah berjuang sebentar, inilah solusi umum.
Kunci untuk solusi saya adalah melalui kode sumber Python dan menyadari bahwa dokumentasi kode (dijelaskan di sini ) sudah menjelaskan cara memperluas yang ada
json.dumps
untuk mendukung tipe data lainnya.Misalkan Anda saat ini memiliki model yang berisi beberapa bidang yang tidak dapat serial ke JSON dan model yang berisi bidang JSON awalnya terlihat seperti ini:
Cukup tentukan kustom
JSONEncoder
seperti ini:Dan kemudian gunakan saja
JSONField
seperti di bawah ini:Kuncinya adalah
default(self, obj)
metode di atas. Untuk setiap satu... is not JSON serializable
keluhan yang Anda terima dari Python, cukup tambahkan kode untuk menangani tipe JSON yang tidak dapat digunakan (sepertiEnum
ataudatetime
)Misalnya, inilah cara saya mendukung kelas yang diturunkan dari
Enum
:Akhirnya, dengan kode yang diimplementasikan seperti di atas, Anda bisa mengonversi model Peewee apa saja menjadi objek JSON-seriazable seperti di bawah ini:
Meskipun kode di atas (agak) khusus untuk Peewee, tapi saya pikir:
json.dumps
kerjanya, solusi ini juga bekerja dengan Python (sans ORM) secara umum jugaAda pertanyaan, silakan posting di bagian komentar. Terima kasih!
sumber
Fungsi ini menggunakan rekursi untuk mengulangi setiap bagian kamus dan memanggil metode repr () dari kelas yang bukan tipe bawaan .
sumber
Ini adalah perpustakaan kecil yang membuat serialisasi objek dengan semua anaknya ke JSON dan juga menguraikannya kembali:
https://github.com/Toubs/PyJSONSerialization/
sumber
Saya datang dengan solusi saya sendiri. Gunakan metode ini, berikan dokumen apa saja ( dict , daftar , ObjectId dll) untuk membuat cerita bersambung.
sumber
Saya memilih untuk menggunakan dekorator untuk memecahkan masalah serialisasi objek datetime. Ini kode saya:
Dengan mengimpor modul di atas, modul saya yang lain menggunakan json dengan cara normal (tanpa menentukan kata kunci default) untuk membuat serialisasi data yang berisi objek waktu tanggal. Kode serializer datetime secara otomatis dipanggil untuk json.dumps dan json.dump.
sumber
Saya paling suka metode Lost Koder. Saya mengalami masalah ketika mencoba untuk meng-serialisasi objek yang lebih kompleks yang anggota / metode tidak dapat serial. Inilah implementasi saya yang bekerja pada lebih banyak objek:
sumber
Jika Anda dapat menginstal paket, saya sarankan mencoba dill , yang berfungsi dengan baik untuk proyek saya. Yang menyenangkan tentang paket ini adalah ia memiliki antarmuka yang sama
pickle
, jadi jika Anda sudah pernah menggunakanpickle
dalam proyek Anda, Anda dapat dengan mudah menggantinya dengandill
dan melihat apakah skrip berjalan, tanpa mengubah kode apa pun. Jadi ini adalah solusi yang sangat murah untuk dicoba!(Anti-pengungkapan penuh: Saya sama sekali tidak berafiliasi dengan dan tidak pernah berkontribusi pada proyek dill.)
Instal paket:
Kemudian edit kode Anda untuk mengimpor
dill
alih-alihpickle
:Jalankan skrip Anda dan lihat apakah itu berfungsi. (Jika ya, Anda mungkin ingin membersihkan kode Anda sehingga Anda tidak lagi membayangi
pickle
nama modul!)Beberapa spesifik pada tipe data yang
dill
dapat dan tidak bisa membuat serial, dari halaman proyek :sumber
Saya melihat tidak disebutkan di sini tentang versi serial atau backcompat, jadi saya akan memposting solusi saya yang telah saya gunakan sebentar. Saya mungkin harus belajar lebih banyak, khususnya Java dan Javascript mungkin lebih matang daripada saya di sini, tetapi begini
https://gist.github.com/andy-d/b7878d0044a4242c0498ed6d67fd50fe
sumber
Untuk menambahkan opsi lain: Anda dapat menggunakan
attrs
paket danasdict
metode.dan untuk mengkonversi kembali
kelas terlihat seperti ini
sumber
Selain jawaban Onur , Anda mungkin ingin berurusan dengan tipe datetime seperti di bawah ini.
(untuk menangani: objek 'datetime.datetime' tidak memiliki pengecualian atribut ' dict '.)
Pemakaian:
sumber
Pertama, kita perlu membuat objek kita kompatibel dengan JSON, jadi kita bisa membuangnya menggunakan modul JSON standar. Saya melakukannya dengan cara ini:
sumber
Membangun Quinten Cabo 's jawabannya :
Perbedaannya adalah
list
dantuple
(berfungsi untuk array NumPy, dll.)__dict__
).float
danNone
sehingga mereka tidak dapat dikonversi ke string.Kiri sebagai latihan untuk pembaca adalah untuk menangani
__slots__
, kelas-kelas yang baik iterable dan memiliki anggota, kelas-kelas yang kamus dan juga memiliki anggota, dll.sumber