
ferhan
Ich versuche, eine JSON-String-Darstellung einer Klasseninstanz zu erstellen, und habe Schwierigkeiten. Nehmen wir an, die Klasse ist wie folgt aufgebaut:
class testclass:
value1 = "a"
value2 = "b"
Ein Aufruf von json.dumps erfolgt wie folgt:
t = testclass()
json.dumps
Es schlägt fehl und sagt mir, dass die Testklasse nicht JSON-serialisierbar ist.
TypeError: <__main__.testclass object at 0x000000000227A400> is not JSON serializable
Ich habe auch versucht, das Pickle-Modul zu verwenden:
t = testclass()
print(pickle.dumps(t, pickle.HIGHEST_PROTOCOL))
Und es gibt Klasseninstanzinformationen, aber keinen serialisierten Inhalt der Klasseninstanz.
b'\x80\x03c__main__\ntestclass\nq\x00)\x81q\x01}q\x02b.'
Was mache ich falsch?

steveha
Das grundlegende Problem ist, dass der JSON-Encoder json.dumps()
weiß nur, wie standardmäßig eine begrenzte Menge von Objekttypen serialisiert wird, alle integrierten Typen. Liste hier: https://docs.python.org/3.3/library/json.html#encoders-and-decoders
Eine gute Lösung wäre, Ihre Klasse erben zu lassen JSONEncoder
und dann die implementieren JSONEncoder.default()
Funktion, und sorgen Sie dafür, dass diese Funktion das richtige JSON für Ihre Klasse ausgibt.
Eine einfache Lösung wäre anzurufen json.dumps()
auf der .__dict__
Mitglied dieser Instanz. Das ist ein Standard-Python dict
und wenn Ihre Klasse einfach ist, ist sie JSON-serialisierbar.
class Foo(object):
def __init__(self):
self.x = 1
self.y = 2
foo = Foo()
s = json.dumps(foo) # raises TypeError with "is not JSON serializable"
s = json.dumps(foo.__dict__) # s set to: {"x":1, "y":2}
Der obige Ansatz wird in diesem Blogbeitrag diskutiert:
Serialisieren beliebiger Python-Objekte in JSON mit _Diktat_
Und natürlich bietet Python eine eingebaute Funktion, die darauf zugreift .__dict__
für dich, genannt vars()
.
Das obige Beispiel kann also auch so gemacht werden:
s = json.dumps(vars(foo)) # s set to: {"x":1, "y":2}
Es gibt einen Weg, der für mich großartig funktioniert und den Sie ausprobieren können:
json.dumps()
kann einen optionalen Parameter annehmen Ursprünglich wo Sie eine benutzerdefinierte Serializer-Funktion für unbekannte Typen angeben können, die in meinem Fall so aussieht
def serialize(obj):
"""JSON serializer for objects not serializable by default json code"""
if isinstance(obj, date):
serial = obj.isoformat()
return serial
if isinstance(obj, time):
serial = obj.isoformat()
return serial
return obj.__dict__
Die ersten beiden ifs sind für die Serialisierung von Datum und Uhrzeit und dann gibt es noch eine obj.__dict__
für jedes andere Objekt zurückgegeben.
Der letzte Aufruf sieht so aus:
json.dumps(myObj, default=serialize)
Es ist besonders gut, wenn Sie eine Sammlung serialisieren und nicht anrufen möchten __dict__
explizit für jedes Objekt. Hier wird es automatisch für Sie erledigt.
Hat bisher so gut für mich funktioniert, ich freue mich auf Ihre Gedanken.

codeman48
Sie können die angeben default
benannter Parameter in der json.dumps()
Funktion:
json.dumps(obj, default=lambda x: x.__dict__)
Erläuterung:
Bilden Sie die Dokumente (2.7, 3.6):
``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.
(Funktioniert auf Python 2.7 und Python 3.x)
Hinweis: In diesem Fall benötigen Sie instance
Variablen und nicht class
Variablen, wie es das Beispiel in der Frage versucht. (Ich nehme an, der Fragesteller meinte class instance
Objekt einer Klasse sein)
Ich habe das zuerst aus der Antwort von @phihag hier gelernt. Fand es die einfachste und sauberste Art, die Arbeit zu erledigen.

gies0r
Verwenden jsonpickle
import jsonpickle
object = YourClass()
json_object = jsonpickle.encode(object)
Ich mache einfach:
data=json.dumps(myobject.__dict__)
Dies ist nicht die vollständige Antwort, und wenn Sie eine komplizierte Objektklasse haben, werden Sie sicherlich nicht alles bekommen. Ich verwende dies jedoch für einige meiner einfachen Objekte.
Eine, mit der es wirklich gut funktioniert, ist die Klasse “Optionen”, die Sie vom OptionParser-Modul erhalten. Hier ist es zusammen mit der JSON-Anforderung selbst.
def executeJson(self, url, options):
data=json.dumps(options.__dict__)
if options.verbose:
print data
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
return requests.post(url, data, headers=headers)
Python3.x
Der beste Ansatz, den ich mit meinem Wissen erreichen konnte, war dieser.
Beachten Sie, dass dieser Code auch set() behandelt.
Dieser Ansatz ist generisch und benötigt lediglich die Erweiterung der Klasse (im zweiten Beispiel).
Beachten Sie, dass ich es nur mit Dateien mache, aber es ist einfach, das Verhalten nach Ihrem Geschmack zu ändern.
Dies ist jedoch ein CoDec.
Mit etwas mehr Arbeit können Sie Ihre Klasse auch auf andere Weise konstruieren. Ich gehe davon aus, dass ein Standardkonstruktor es instanziiert, und aktualisiere dann die Klasse dict.
import json
import collections
class JsonClassSerializable(json.JSONEncoder):
REGISTERED_CLASS = {}
def register(ctype):
JsonClassSerializable.REGISTERED_CLASS[ctype.__name__] = ctype
def default(self, obj):
if isinstance(obj, collections.Set):
return dict(_set_object=list(obj))
if isinstance(obj, JsonClassSerializable):
jclass = {}
jclass["name"] = type(obj).__name__
jclass["dict"] = obj.__dict__
return dict(_class_object=jclass)
else:
return json.JSONEncoder.default(self, obj)
def json_to_class(self, dct):
if '_set_object' in dct:
return set(dct['_set_object'])
elif '_class_object' in dct:
cclass = dct['_class_object']
cclass_name = cclass["name"]
if cclass_name not in self.REGISTERED_CLASS:
raise RuntimeError(
"Class {} not registered in JSON Parser"
.format(cclass["name"])
)
instance = self.REGISTERED_CLASS[cclass_name]()
instance.__dict__ = cclass["dict"]
return instance
return dct
def encode_(self, file):
with open(file, 'w') as outfile:
json.dump(
self.__dict__, outfile,
cls=JsonClassSerializable,
indent=4,
sort_keys=True
)
def decode_(self, file):
try:
with open(file, 'r') as infile:
self.__dict__ = json.load(
infile,
object_hook=self.json_to_class
)
except FileNotFoundError:
print("Persistence load failed "
"'{}' do not exists".format(file)
)
class C(JsonClassSerializable):
def __init__(self):
self.mill = "s"
JsonClassSerializable.register(C)
class B(JsonClassSerializable):
def __init__(self):
self.a = 1230
self.c = C()
JsonClassSerializable.register(B)
class A(JsonClassSerializable):
def __init__(self):
self.a = 1
self.b = {1, 2}
self.c = B()
JsonClassSerializable.register(A)
A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
print(b.b)
print(b.c.a)
Bearbeiten
Mit etwas mehr Recherche fand ich einen Weg, ohne die Notwendigkeit des zu verallgemeinern SUPERKLASSE Methodenaufruf registrieren, mit a Metaklasse
import json
import collections
REGISTERED_CLASS = {}
class MetaSerializable(type):
def __call__(cls, *args, **kwargs):
if cls.__name__ not in REGISTERED_CLASS:
REGISTERED_CLASS[cls.__name__] = cls
return super(MetaSerializable, cls).__call__(*args, **kwargs)
class JsonClassSerializable(json.JSONEncoder, metaclass=MetaSerializable):
def default(self, obj):
if isinstance(obj, collections.Set):
return dict(_set_object=list(obj))
if isinstance(obj, JsonClassSerializable):
jclass = {}
jclass["name"] = type(obj).__name__
jclass["dict"] = obj.__dict__
return dict(_class_object=jclass)
else:
return json.JSONEncoder.default(self, obj)
def json_to_class(self, dct):
if '_set_object' in dct:
return set(dct['_set_object'])
elif '_class_object' in dct:
cclass = dct['_class_object']
cclass_name = cclass["name"]
if cclass_name not in REGISTERED_CLASS:
raise RuntimeError(
"Class {} not registered in JSON Parser"
.format(cclass["name"])
)
instance = REGISTERED_CLASS[cclass_name]()
instance.__dict__ = cclass["dict"]
return instance
return dct
def encode_(self, file):
with open(file, 'w') as outfile:
json.dump(
self.__dict__, outfile,
cls=JsonClassSerializable,
indent=4,
sort_keys=True
)
def decode_(self, file):
try:
with open(file, 'r') as infile:
self.__dict__ = json.load(
infile,
object_hook=self.json_to_class
)
except FileNotFoundError:
print("Persistence load failed "
"'{}' do not exists".format(file)
)
class C(JsonClassSerializable):
def __init__(self):
self.mill = "s"
class B(JsonClassSerializable):
def __init__(self):
self.a = 1230
self.c = C()
class A(JsonClassSerializable):
def __init__(self):
self.a = 1
self.b = {1, 2}
self.c = B()
A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
# 1
print(b.b)
# {1, 2}
print(b.c.a)
# 1230
print(b.c.c.mill)
# s

Brendan Holz
JSON ist nicht wirklich dafür gedacht, beliebige Python-Objekte zu serialisieren. Es eignet sich hervorragend zum Serialisieren dict
Objekte, aber die pickle
Modul ist wirklich das, was Sie im Allgemeinen verwenden sollten. Ausgabe von pickle
ist nicht wirklich menschenlesbar, aber es sollte gut entpicken. Wenn Sie darauf bestehen, JSON zu verwenden, können Sie sich die jsonpickle
Modul, was ein interessanter hybrider Ansatz ist.
https://github.com/jsonpickle/jsonpickle
10055300cookie-checkKlasseninstanz in JSON serialisierenyes
stackoverflow.com/questions/2343535/…
– CodeClown42
20. April 2012 um 19:10 Uhr
Verwenden Sie eine Zeile,
s = json.dumps(obj, default=lambda x: x.__dict__)
um die Instanzvariablen des Objekts zu serialisieren (self.value1
,self.value2
, …). Es ist der einfachste und direkteste Weg. Es wird verschachtelte Objektstrukturen serialisieren. Diedefault
Die Funktion wird aufgerufen, wenn ein bestimmtes Objekt nicht direkt serialisierbar ist. Sie können sich auch meine Antwort unten ansehen. Ich fand die populären Antworten unnötig komplex, die wahrscheinlich schon vor langer Zeit wahr waren.– codeman48
13. Dezember 2017 um 7:25 Uhr
Dein
testclass
hat keine__init__()
-Methode, sodass alle Instanzen dieselben zwei Klassenattribute (value1
undvalue2
) in der class-Anweisung definiert. Verstehen Sie den Unterschied zwischen einer Klasse und einer Instanz einer Klasse?– Martinau
30. Januar 2018 um 2:44 Uhr
Dafür gibt es eine Python-Bibliothek github.com/jsonpickle/jsonpickle (Kommentieren, da die Antwort zu unten im Thread ist und nicht erreichbar ist.)
– schöne Grüße
10. Mai 2019 um 4:54 Uhr