Jak uczynić klasę Python serializowalną?
Prosta klasa:
class FileItem:
def __init__(self, fname):
self.fname = fname
Co powinienem zrobić, aby uzyskać wynik:
>>> import json
>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable
Bez błędu
python
json
serialization
Siergiej
źródło
źródło
import jsons
patrz odpowiedź poniżej - działa idealnieOdpowiedzi:
Czy masz pojęcie o oczekiwanej wydajności? Na przykład czy to zrobi?
W takim przypadku możesz po prostu zadzwonić
json.dumps(f.__dict__)
.Jeśli chcesz uzyskać bardziej spersonalizowane dane wyjściowe, będziesz musiał podklasować
JSONEncoder
i wdrożyć własną niestandardową serializację.Trywialny przykład, patrz poniżej.
Następnie przekazujesz tę klasę do
json.dumps()
metody jakocls
kwarg:Jeśli chcesz również zdekodować, musisz dostarczyć niestandardowe
object_hook
doJSONDecoder
klasy. Na przykładźródło
__dict__
nie będzie działać we wszystkich przypadkach. Jeśli atrybuty nie zostały ustawione po utworzeniu instancji obiektu,__dict__
być może nie zostaną w pełni wypełnione. W powyższym przykładzie wszystko jest w porządku, ale jeśli masz atrybuty klasy, które również chcesz zakodować, nie zostaną one wymienione,__dict__
chyba że zostały zmodyfikowane w__init__
wywołaniu klasy lub w inny sposób po utworzeniu wystąpienia obiektu.from_json()
funkcja użyta jako hook obiektu powinna miećelse: return json_object
instrukcję, aby mogła również obsługiwać obiekty ogólne.__dict__
również nie działa, jeśli używasz__slots__
nowej klasy stylu.JSONEncoder
jak powyżej, aby utworzyć niestandardowy protokół, na przykład sprawdzając istnienie__json_serializable__
metody i wywołując ją w celu uzyskania reprezentatywnej reprezentacji obiektu przez JSON. Byłoby to zgodne z innymi wzorcami Python, jak__getitem__
,__str__
,__eq__
, i__len__
.__dict__
również nie będzie działał rekurencyjnie, np. jeśli atrybut twojego obiektu jest innym obiektem.Oto proste rozwiązanie prostej funkcji:
.toJSON()
metodaZamiast klasy szeregowalnej JSON, zaimplementuj metodę serializatora:
Więc po prostu zadzwoń do serializacji:
wyświetli:
źródło
o.__dict___
. Spróbuj własnego przykładu: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
instancjami. Zgłasza następujący błąd:'datetime.datetime' object has no attribute '__dict__'
W przypadku bardziej złożonych klas można rozważyć użycie narzędzia jsonpickle :
(link do jsonpickle na PyPi)
źródło
jsonpickle
obiekt. Ponadto nie było w stanie dekodować dykta zawierającego ramki danych pand.obj = jsonpickle.decode(file.read())
ifile.write(jsonpickle.encode(obj))
.Większość odpowiedzi wymaga zmiany wywołania na json.dumps () , co nie zawsze jest możliwe lub pożądane (może się to zdarzyć na przykład wewnątrz komponentu frameworka).
Jeśli chcesz mieć możliwość wywoływania json.dumps (obj) w obecnej postaci, to proste rozwiązanie dziedziczy po dict :
Działa to, jeśli twoja klasa jest po prostu podstawową reprezentacją danych, dla trudniejszych rzeczy zawsze możesz ustawić klucze jawnie.
źródło
dumps
nie jest dobrym rozwiązaniem. Nawiasem mówiąc, w większości przypadków prawdopodobnie chcesz miećdict
dziedziczenie wraz z delegowaniem, co oznacza, że będziesz mieć jakiśdict
atrybut typu w swojej klasie, a następnie przekażesz ten atrybut jako parametr jako inicjalizację coś w stylusuper().__init__(self.elements)
.Podoba mi się odpowiedź Onura, ale rozszerzyłbym ją o opcjonalną
toJSON()
metodę dla obiektów do serializacji siebie:źródło
json.dumps
a wprowadzeniem niestandardowej obsługi. Dzięki!try-catch
zrobiłby czegoś takiego jakif 'toJSON' in obj.__attrs__():
... aby uniknąć cichej awarii (w przypadku awarii funkcji toJSON () z innego powodu niż jej brak) ... awarii, która potencjalnie prowadzi do uszkodzenia danych.Inną opcją jest zawinięcie zrzutu JSON we własną klasę:
Lub jeszcze lepiej, podklasowanie klasy FileItem z
JsonSerializable
klasy:Testowanie:
źródło
__json__encode__
/__json_decode__
(ujawnienie: zrobiłem ostatni).Po prostu dodaj
to_json
metodę do swojej klasy w ten sposób:I dodaj ten kod (z tej odpowiedzi ) , gdzieś na górze wszystkiego:
Spowoduje to załatanie małpiego modułu json podczas importu, więc JSONEncoder.default () automatycznie sprawdza specjalną metodę „to_json ()” i używa go do kodowania obiektu, jeśli zostanie znaleziony.
Tak jak powiedział Onur, ale tym razem nie musisz aktualizować wszystkich
json.dumps()
w swoim projekcie.źródło
TheObject.to_json = my_serializer
.Tego dnia natknąłem się na ten problem i zaimplementowałem bardziej ogólną wersję Enkodera dla obiektów Python, która może obsługiwać obiekty zagnieżdżone i pola dziedziczone :
Przykład:
Wynik:
źródło
return obj
w ostatniej linii to zrobiłemreturn super(ObjectEncoder, self).default(obj)
. Odniesienie TUTAJJeśli używasz Python3.5 +, możesz użyć
jsons
. Konwertuje Twój obiekt (i wszystkie jego atrybuty rekurencyjnie) na dyktando.Lub jeśli chcesz ciąg:
Lub jeśli twoja klasa zaimplementowała
jsons.JsonSerializable
:źródło
jsons
biblioteki z klasami danych . Jak dotąd, tak dobrze dla mnie!jeśli używasz standardu
json
, musisz zdefiniowaćdefault
funkcjęźródło
json.dumps(User('alice', '[email protected]'), default=lambda x: x.__dict__)
json
jest ograniczony pod względem obiektów, które może wydrukować, ijsonpickle
(być może potrzebujeszpip install jsonpickle
) jest ograniczony pod względem, że nie może wciąć tekstu. Jeśli chcesz sprawdzić zawartość obiektu, którego klasy nie możesz zmienić, nadal nie mogę znaleźć prostszego sposobu niż:Uwaga: nadal nie mogą wydrukować metod obiektowych.
źródło
Ta klasa może załatwić sprawę, konwertuje obiekt na standardowy json.
stosowanie:
pracuje w
python2.7
ipython3
.źródło
źródło
default(obj)
jest funkcją, która powinna zwrócić możliwą do serializacji wersję obj lub wywołać TypeError. Domyślniedefault
po prostu podnosi TypeError.jaraco udzielił dość schludnej odpowiedzi. Musiałem naprawić kilka drobnych rzeczy, ale to działa:
Kod
Pamiętaj, że do załadowania potrzebujemy dwóch kroków. Na razie
__python__
właściwość nie jest używana.Jak często to się dzieje?
Korzystając z metody AlJohri , sprawdzam popularność podejść:
Serializacja (Python -> JSON):
to_json
: 266,595 w dniu 27.06.2018toJSON
: 96.307 27.06.2018__json__
: 8.504 w dniu 27.06.2018for_json
: 6937 na 27.06.2018Deserializacja (JSON -> Python):
from_json
: 226,101 w dniu 27 czerwca 2018 rźródło
To zadziałało dla mnie dobrze:
i wtedy
i
źródło
Jeśli nie przeszkadza ci instalacja pakietu, możesz użyć sztuczek json :
Potem wystarczy importu
dump(s)
zjson_tricks
zamiast JSON, i to będzie zazwyczaj praca:co da
I to w gruncie rzeczy!
To ogólnie będzie działać świetnie. Istnieją pewne wyjątki, np. Jeśli zdarzają się specjalne rzeczy lub dzieje się
__new__
więcej magii metaklasy.Oczywiście ładowanie również działa (w przeciwnym razie o co chodzi):
Zakłada się, że
module_name.test_class.MyTestCls
można go zaimportować i nie zmienił się w niekompatybilny sposób. Otrzymasz instancję , a nie słownik lub coś takiego, i powinna to być identyczna kopia do tej, którą zrzuciłeś.Jeśli chcesz dostosować sposób (de) serializacji czegoś, możesz dodać specjalne metody do swojej klasy, na przykład:
który serializuje tylko część parametrów atrybutów, jako przykład.
Jako darmowy bonus otrzymujesz (de) serializację tablic numpy, daty i godziny, uporządkowanych map, a także możliwość dołączania komentarzy do JSON.
Uwaga: Stworzyłem json_tricks , ponieważ miałem ten sam problem co ty.
źródło
jsonweb wydaje mi się najlepszym rozwiązaniem dla mnie. Zobacz http://www.jsonweb.info/en/latest/
źródło
Oto moje 3 centy ...
To pokazuje wyraźną serializację json dla drzewiastego obiektu python.
Uwaga: Jeśli naprawdę potrzebujesz takiego kodu, możesz użyć skręconej klasy FilePath .
źródło
Zetknąłem się z tym problemem, gdy próbowałem zapisać model Peewee w PostgreSQL
JSONField
.Po krótkich zmaganiach oto ogólne rozwiązanie.
Kluczem do mojego rozwiązania jest przejrzenie kodu źródłowego Pythona i uświadomienie sobie, że dokumentacja kodu (opisana tutaj ) już wyjaśnia, jak rozszerzyć istniejący,
json.dumps
aby obsługiwał inne typy danych.Załóżmy, że masz model, który zawiera niektóre pola, które nie są serializowane do JSON, a model zawierający pole JSON początkowo wygląda następująco:
Po prostu zdefiniuj niestandardowy
JSONEncoder
taki sposób:A potem po prostu użyj go
JSONField
jak poniżej:Kluczem jest
default(self, obj)
powyższa metoda. Do każdej... is not JSON serializable
skargi otrzymywanej od Pythona wystarczy dodać kod, aby obsłużyć typ, który nie może zostać przekształcony w JSON (taki jakEnum
lubdatetime
)Na przykład oto, jak popieram klasę dziedziczącą z
Enum
:Wreszcie, dzięki zaimplementowanemu kodowi, jak powyżej, możesz po prostu przekonwertować dowolne modele Peewee na obiekt, który może być seriazowalny w JSON, jak poniżej:
Chociaż powyższy kod był (nieco) specyficzny dla Peewee, ale myślę:
json.dumps
działa, to rozwiązanie działa również ogólnie z Pythonem (bez ORM)Wszelkie pytania prosimy pisać w sekcji komentarzy. Dzięki!
źródło
Ta funkcja używa rekurencji do iteracji po każdej części słownika, a następnie wywołuje metody repr () klas, które nie są typami wbudowanymi.
źródło
To jest mała biblioteka, która serializuje obiekt wraz ze swoimi dziećmi do JSON, a także analizuje go z powrotem:
https://github.com/Toubs/PyJSONSerialization/
źródło
Wymyśliłem własne rozwiązanie. Użyj tej metody, przekaż dowolny dokument ( dict , list , ObjectId itp.) Do serializacji.
źródło
Zdecydowałem się użyć dekoratorów do rozwiązania problemu serializacji obiektów z datetime. Oto mój kod:
Importując powyższy moduł, moje inne moduły używają json w normalny sposób (bez określania domyślnego słowa kluczowego) do serializacji danych zawierających obiekty daty i godziny. Kod serializatora datetime jest automatycznie wywoływany dla json.dumps i json.dump.
źródło
Najbardziej podobała mi się metoda Lost Koder. Wystąpiły problemy podczas próby serializacji bardziej złożonych obiektów, których członkowie / metody nie są serializowane. Oto moja implementacja, która działa na większej liczbie obiektów:
źródło
Jeśli jesteś w stanie zainstalować pakiet, polecam wypróbować dill , który zadziałał dobrze w moim projekcie. Fajną rzeczą w tym pakiecie jest to, że ma ten sam interfejs co
pickle
, więc jeśli już używałeśpickle
w swoim projekcie, możesz po prostu podstawićdill
i zobaczyć, czy skrypt działa, bez zmiany kodu. Jest to więc bardzo tanie rozwiązanie do wypróbowania!(Pełne zapobieganie ujawnieniu: nie jestem w żaden sposób związany z projektem koperku i nigdy nie brałem w nim udziału).
Zainstaluj pakiet:
Następnie edytuj kod do zaimportowania
dill
zamiastpickle
:Uruchom skrypt i sprawdź, czy działa. (Jeśli tak, możesz wyczyścić kod, aby nie wyświetlać
pickle
nazwy modułu!)Niektóre szczegóły dotyczące typów danych, które
dill
mogą i nie mogą serializować, na stronie projektu :źródło
Nie widzę tu żadnej wzmianki o wersjonowaniu seryjnym ani wstecznym, więc opublikuję moje rozwiązanie, z którego korzystałem przez chwilę. Prawdopodobnie mam o wiele więcej do nauczenia się, szczególnie Java i JavaScript są prawdopodobnie bardziej dojrzałe ode mnie tutaj, ale proszę bardzo
https://gist.github.com/andy-d/b7878d0044a4242c0498ed6d67fd50fe
źródło
Aby dodać inną opcję: Możesz użyć
attrs
pakietu iasdict
metody.i do konwersji
klasa wygląda tak
źródło
Oprócz odpowiedzi Onura , być może chcesz poradzić sobie z typem daty i godziny , jak poniżej.
(w celu obsługi: obiekt „datetime.datetime” nie ma wyjątku atrybutu „ dict ”).
Stosowanie:
źródło
Najpierw musimy uczynić nasz obiekt zgodny z JSON, abyśmy mogli go zrzucić za pomocą standardowego modułu JSON. Zrobiłem to w ten sposób:
źródło
Opierając się na Quinten Cabo „s odpowiedź :
Różnice są
list
ituple
(działa dla tablic NumPy itp.)__dict__
).float
iNone
tak nie zamieniony na ciąg.Zadaniem czytelnika pozostaje
__slots__
poradzenie sobie z klasami, które są iterowalne i mają członków, klasy, które są słownikami, a także mają członków itp.źródło