Wie mache ich eine Python-Klasse serialisierbar?
Eine einfache Klasse:
class FileItem:
def __init__(self, fname):
self.fname = fname
Was soll ich tun, um eine Ausgabe zu erhalten:
>>> import json
>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable
Ohne den Fehler
python
json
serialization
Sergey
quelle
quelle
import jsons
siehe Antwort unten - es funktioniert einwandfreiAntworten:
Haben Sie eine Vorstellung von der erwarteten Leistung? Zum Beispiel reicht das?
In diesem Fall können Sie lediglich anrufen
json.dumps(f.__dict__)
.Wenn Sie eine individuellere Ausgabe wünschen, müssen Sie eine Unterklasse erstellen
JSONEncoder
und Ihre eigene benutzerdefinierte Serialisierung implementieren.Ein einfaches Beispiel finden Sie unten.
Dann übergeben Sie diese Klasse
json.dumps()
alscls
kwarg an die Methode :Wenn Sie auch dekodieren möchten, müssen Sie
object_hook
derJSONDecoder
Klasse eine benutzerdefinierte Datei bereitstellen . Zum Beispielquelle
__dict__
funktioniert nicht in allen Fällen. Wenn die Attribute nach der Instanziierung des Objekts nicht festgelegt wurden, werden sie__dict__
möglicherweise nicht vollständig ausgefüllt. Im obigen Beispiel sind Sie in Ordnung. Wenn Sie jedoch Klassenattribute haben, die Sie ebenfalls codieren möchten, werden diese nicht aufgelistet, es__dict__
sei denn, sie wurden im__init__
Aufruf der Klasse oder auf andere Weise geändert , nachdem das Objekt instanziiert wurde.from_json()
als Objekt-Hook verwendete Funktion sollte eineelse: return json_object
Anweisung haben, damit sie auch allgemeine Objekte behandeln kann.__dict__
funktioniert auch nicht, wenn Sie__slots__
eine neue Stilklasse verwenden .JSONEncoder
ein benutzerdefiniertes Protokoll wie oben verwenden, um ein benutzerdefiniertes Protokoll zu erstellen, z. B. das Vorhandensein einer__json_serializable__
Methode zu überprüfen und es aufzurufen, um eine serialisierbare JSON-Darstellung des Objekts zu erhalten. Dies würde mit anderem Python - Muster im Einklang sein, wie__getitem__
,__str__
,__eq__
, und__len__
.__dict__
funktioniert auch nicht rekursiv, z. B. wenn ein Attribut Ihres Objekts ein anderes Objekt ist.Hier ist eine einfache Lösung für eine einfache Funktion:
.toJSON()
MethodeImplementieren Sie anstelle einer serialisierbaren JSON-Klasse eine Serialisierungsmethode:
Sie rufen es einfach auf, um zu serialisieren:
wird ausgegeben:
quelle
o.__dict___
. Versuchen Sie Ihr eigenes Beispiel: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
Instanzen. Es wirft den folgenden Fehler aus:'datetime.datetime' object has no attribute '__dict__'
Für komplexere Klassen können Sie das Tool jsonpickle in Betracht ziehen :
(Link zu jsonpickle auf PyPi)
quelle
jsonpickle
Objekt codiert und decodiert wird. Dies war auch nicht in der Lage, ein Diktat von Diktaten zu entschlüsseln, die Pandas-Datenrahmen enthielten.obj = jsonpickle.decode(file.read())
undfile.write(jsonpickle.encode(obj))
.Die meisten Antworten beinhalten das Ändern des Aufrufs in json.dumps () , was nicht immer möglich oder wünschenswert ist (dies kann beispielsweise innerhalb einer Framework-Komponente geschehen).
Wenn Sie json.dumps (obj) so wie es ist aufrufen möchten, erbt eine einfache Lösung von dict :
Dies funktioniert, wenn Ihre Klasse nur eine grundlegende Datendarstellung ist. Für schwierigere Dinge können Sie Schlüssel immer explizit festlegen.
quelle
dumps
keine gute Lösung ist. Übrigens, in den meisten Fällen möchten Sie wahrscheinlich einedict
Vererbung zusammen mit einer Delegierung haben, was bedeutet, dass Sie eindict
Typattribut in Ihrer Klasse haben. Anschließend übergeben Sie dieses Attribut als Parameter als Initialisierungsuper().__init__(self.elements)
.Ich mag Onurs Antwort , würde sie aber um eine optionale
toJSON()
Methode erweitern, mit der Objekte sich selbst serialisieren können:quelle
json.dumps
und der Einführung einer benutzerdefinierten Handhabung ist. Vielen Dank!try-catch
wahrscheinlich etwas zu tun wieif 'toJSON' in obj.__attrs__():
... um einen stillen Fehler zu vermeiden (im Falle eines Fehlers in toJSON () aus einem anderen Grund als wenn es nicht vorhanden ist) ... einen Fehler, der möglicherweise zu einer Beschädigung der Daten führt.Eine andere Option besteht darin, JSON-Dumping in eine eigene Klasse zu packen:
Oder, noch besser, die FileItem-Klasse einer
JsonSerializable
Klasse unterzuordnen :Testen:
quelle
__json__encode__
/ ändern können__json_decode__
(Offenlegung: Ich habe die letzte erstellt).Fügen Sie
to_json
Ihrer Klasse einfach folgende Methode hinzu :Und fügen Sie diesen Code (aus dieser Antwort ) irgendwo oben hinzu:
Dadurch wird das JSON-Modul beim Import mit einem Affen-Patch versehen, sodass JSONEncoder.default () automatisch nach einer speziellen "to_json ()" - Methode sucht und diese zum Codieren des Objekts verwendet, wenn es gefunden wird.
Genau wie Onur sagte, aber dieses Mal müssen Sie nicht alle
json.dumps()
in Ihrem Projekt aktualisieren .quelle
TheObject.to_json = my_serializer
.Ich bin neulich auf dieses Problem gestoßen und habe eine allgemeinere Version eines Encoders für Python-Objekte implementiert, der verschachtelte Objekte und geerbte Felder verarbeiten kann :
Beispiel:
Ergebnis:
quelle
return obj
in der letzten Zeile habe ich das getanreturn super(ObjectEncoder, self).default(obj)
. Referenz HIERWenn Sie Python3.5 + verwenden, können Sie verwenden
jsons
. Es konvertiert Ihr Objekt (und alle seine Attribute rekursiv) in ein Diktat.Oder wenn Sie eine Zeichenfolge wollten:
Oder wenn Ihre Klasse implementiert
jsons.JsonSerializable
:quelle
jsons
Bibliothek mit Datenklassen zu mischen . So weit, so gut für mich!Wenn Sie Standard verwenden
json
, müssen Sie einedefault
Funktion definierenquelle
json.dumps(User('alice', '[email protected]'), default=lambda x: x.__dict__)
json
ist in Bezug auf Objekte, die gedruckt werden können, begrenzt, undjsonpickle
(möglicherweise benötigen Sie apip install jsonpickle
) ist in Bezug auf Objekte, die keinen Text einrücken können, begrenzt. Wenn Sie den Inhalt eines Objekts untersuchen möchten, dessen Klasse Sie nicht ändern können, konnte ich immer noch keinen besseren Weg finden als:Hinweis: Die Objektmethoden können immer noch nicht gedruckt werden.
quelle
Diese Klasse kann den Trick machen, sie konvertiert das Objekt in Standard-JSON.
Verwendungszweck:
Arbeiten in
python2.7
undpython3
.quelle
quelle
default(obj)
ist eine Funktion, die eine serialisierbare Version von obj zurückgeben oder TypeError auslösen soll. Die Standardeinstellungdefault
löst einfach TypeError aus.jaraco gab eine ziemlich ordentliche Antwort. Ich musste einige kleinere Dinge reparieren, aber das funktioniert:
Code
Beachten Sie, dass wir zum Laden zwei Schritte benötigen. Derzeit wird die
__python__
Eigenschaft nicht verwendet.Wie häufig ist das?
Mit der Methode von AlJohri überprüfe ich die Popularität von Ansätzen:
Serialisierung (Python -> JSON):
to_json
: 266.595 am 27.06.2018toJSON
: 96,307 am 27.06.2018__json__
: 8,504 am 27.06.2018for_json
: 6.937 am 27.06.2018Deserialisierung (JSON -> Python):
from_json
: 226,101 am 27.06.2018quelle
Das hat bei mir gut funktioniert:
und dann
und
quelle
Wenn es Ihnen nichts ausmacht, ein Paket dafür zu installieren, können Sie json-Tricks verwenden :
Danach müssen Sie nur noch
dump(s)
vonjson_tricks
statt json importieren , und es funktioniert normalerweise:was geben wird
Und das war's auch schon!
Dies wird im Allgemeinen gut funktionieren. Es gibt einige Ausnahmen, z. B. wenn besondere Dinge passieren
__new__
oder mehr Metaklassenmagie stattfindet.Natürlich funktioniert das Laden auch (ansonsten, worum geht es):
Dies setzt voraus, dass
module_name.test_class.MyTestCls
es importiert werden kann und sich nicht auf nicht kompatible Weise geändert hat. Sie erhalten eine Instanz zurück , kein Wörterbuch oder ähnliches, und es sollte eine identische Kopie sein wie die, die Sie ausgegeben haben.Wenn Sie anpassen möchten, wie etwas (de) serialisiert wird, können Sie Ihrer Klasse spezielle Methoden hinzufügen, z.
Dies serialisiert beispielsweise nur einen Teil der Attributparameter.
Als kostenlosen Bonus erhalten Sie eine (De-) Serialisierung von Numpy-Arrays, Datum und Uhrzeit, geordneten Karten sowie die Möglichkeit, Kommentare in json aufzunehmen.
Haftungsausschluss: Ich habe json_tricks erstellt , weil ich das gleiche Problem hatte wie Sie.
quelle
jsonweb scheint die beste Lösung für mich zu sein. Siehe http://www.jsonweb.info/en/latest/
quelle
Hier sind meine 3 Cent ...
Dies demonstriert die explizite JSON-Serialisierung für ein baumartiges Python-Objekt.
Hinweis: Wenn Sie tatsächlich einen solchen Code wünschen, können Sie die verdrehte FilePath- Klasse verwenden.
quelle
Ich bin auf dieses Problem gestoßen, als ich versucht habe, Peewees Modell in PostgreSQL zu speichern
JSONField
.Hier ist die allgemeine Lösung, nachdem Sie eine Weile gekämpft haben.
Der Schlüssel zu meiner Lösung besteht darin, den Quellcode von Python durchzugehen und zu erkennen, dass in der Codedokumentation ( hier beschrieben ) bereits erläutert wird, wie der vorhandene Code erweitert werden kann
json.dumps
, um andere Datentypen zu unterstützen.Angenommen, Sie haben derzeit ein Modell, das einige Felder enthält, die nicht für JSON serialisierbar sind, und das Modell, das das JSON-Feld enthält, sieht ursprünglich folgendermaßen aus:
Definieren Sie einfach einen Benutzer
JSONEncoder
wie diesen:Und dann benutze es einfach
JSONField
wie folgt:Der Schlüssel ist die
default(self, obj)
obige Methode.... is not JSON serializable
Fügen Sie für jede einzelne Beschwerde, die Sie von Python erhalten, einfach Code hinzu, um den Typ "unserializable to JSON" (z. B.Enum
oder) zu verarbeitendatetime
) zu verarbeiten.So unterstütze ich beispielsweise eine Klasse, die von Folgendes erbt
Enum
:Schließlich können Sie mit dem wie oben implementierten Code einfach alle Peewee-Modelle in ein JSON-seriazierbares Objekt wie das folgende konvertieren:
Obwohl der obige Code (etwas) spezifisch für Peewee war, denke ich:
json.dumps
funktioniert, funktioniert diese Lösung auch mit Python (ohne ORM) im AllgemeinenBei Fragen bitte in den Kommentaren posten. Vielen Dank!
quelle
Diese Funktion verwendet die Rekursion, um jeden Teil des Wörterbuchs zu durchlaufen, und ruft dann die repr () -Methoden von Klassen auf, die keine integrierten Typen sind.
quelle
Dies ist eine kleine Bibliothek, die ein Objekt mit all seinen untergeordneten Elementen in JSON serialisiert und es auch zurück analysiert:
https://github.com/Toubs/PyJSONSerialization/
quelle
Ich habe meine eigene Lösung gefunden. Verwenden Sie diese Methode und übergeben Sie ein beliebiges Dokument ( Diktat , Liste , Objekt-ID usw.) zur Serialisierung.
quelle
Ich entschied mich für die Verwendung von Dekoratoren, um das Problem der Serialisierung von datetime-Objekten zu lösen. Hier ist mein Code:
Durch den Import des obigen Moduls verwenden meine anderen Module json auf normale Weise (ohne Angabe des Standardschlüsselworts), um Daten zu serialisieren, die Datums- und Uhrzeitobjekte enthalten. Der datetime-Serializer-Code wird automatisch für json.dumps und json.dump aufgerufen.
quelle
Ich mochte die Methode von Lost Koder am meisten. Beim Versuch, komplexere Objekte zu serialisieren, deren Mitglieder / Methoden nicht serialisierbar sind, sind Probleme aufgetreten. Hier ist meine Implementierung, die für mehr Objekte funktioniert:
quelle
Wenn Sie in der Lage sind, ein Paket zu installieren, würde ich empfehlen, Dill zu versuchen , was für mein Projekt gut funktioniert hat. Das Schöne an diesem Paket ist, dass es die gleiche Oberfläche hat wie
pickle
. Wenn Sie es also bereitspickle
in Ihrem Projekt verwendet haben, können Sie es einfach ersetzendill
und prüfen, ob das Skript ausgeführt wird, ohne Code zu ändern. Es ist also eine sehr billige Lösung, es zu versuchen!(Vollständige Geheimhaltung: Ich bin in keiner Weise mit dem Dill-Projekt verbunden und habe noch nie dazu beigetragen.)
Installieren Sie das Paket:
Bearbeiten Sie dann Ihren zu importierenden Code
dill
anstelle vonpickle
:Führen Sie Ihr Skript aus und prüfen Sie, ob es funktioniert. (Wenn dies der Fall ist, möchten Sie möglicherweise Ihren Code bereinigen, damit Sie den Code nicht mehr beschatten
pickle
!)Einige Details zu Datentypen,
dill
die serialisiert werden können und nicht, finden Sie auf der Projektseite :quelle
Ich sehe hier keine Erwähnung von serieller Versionierung oder Backcompat, daher werde ich meine Lösung veröffentlichen, die ich seit einiger Zeit verwende. Ich kann wahrscheinlich noch viel mehr lernen, insbesondere Java und Javascript sind hier wahrscheinlich reifer als ich, aber hier geht es weiter
https://gist.github.com/andy-d/b7878d0044a4242c0498ed6d67fd50fe
quelle
So fügen Sie eine weitere Option hinzu: Sie können das
attrs
Paket und dieasdict
Methode verwenden.und zurück zu konvertieren
Klasse sieht so aus
quelle
Zusätzlich zur Antwort des Onur möchten Sie möglicherweise den Datums- / Uhrzeittyp wie unten behandeln.
(Um zu behandeln: Das Objekt 'datetime.datetime' hat keine Attributausnahme ' dict '.)
Verwendungszweck:
quelle
Zuerst müssen wir unser Objekt JSON-kompatibel machen, damit wir es mit dem Standard-JSON-Modul sichern können. Ich habe es so gemacht:
quelle
Aufbauend auf Quinten Cabo ‚s Antwort :
Die Unterschiede sind
list
undtuple
((es funktioniert für NumPy-Arrays usw.)__dict__
).float
und wirdNone
daher nicht in Zeichenfolgen konvertiert.Als Übung für den Leser bleibt es
__slots__
, Klassen zu behandeln , die sowohl iterierbar sind als auch Mitglieder haben, Klassen, die Wörterbücher sind und auch Mitglieder haben usw.quelle