Seite 1 von 1
JSON wiedereinlesen mit eigenen Objekten
Verfasst: Samstag 11. Februar 2012, 06:26
von palango
Hey,
ich versuche gerade einen JSON serialisierer für eine eigene Klasse zu schreiben. Das ist der Code:
Code: Alles auswählen
def to_json(pobject):
if isinstance(pobject, Catalog):
data = {"name": pobject.name, "comment": pobject.comment}
return {'__class__': "Catalog",
'__value__': {"metadata": data, "content": pobject.words}}
raise TypeError(repr(pobject) + ' is not JSON serializable')
def from_json(json_object):
if "__class__" in json_object and json_object["__class__"] == "Catalog":
data = json_object["__value__"]
metadata = data["metadata"]
result = Catalog(metadata["name"], metadata["comment"])
result.words = data["content"]
return result
Beim Versuch das zu nutzen bekomme ich immer diesen Fehler.
Code: Alles auswählen
File "catalog.py", line 31, in from_json
metadata = data["metadata"]
TypeError: 'NoneType' object is not subscriptable
Sieht jemand den fehler oder weiß wie ich die zurückgegebenen Maps zu Python Maps umwandeln kann?
Danke!
Re: JSON wiedereinlesen mit eigenen Objekten
Verfasst: Samstag 11. Februar 2012, 09:34
von BlackJack
@palango: Kann ich nicht nachvollziehen. Das hier läuft bei mir problemlos durch:
Code: Alles auswählen
from pprint import pprint
class Catalog(object):
def __init__(self, name, comment):
self.name = name
self.comment = comment
self.words = 'Worte'
def __repr__(self):
return '<%s name:%r comment:%r words:%s>' % (
self.__class__.__name__, self.name, self.comment, self.words
)
def to_json(pobject):
if isinstance(pobject, Catalog):
data = {'name': pobject.name, 'comment': pobject.comment}
return {'__class__': 'Catalog',
'__value__': {'metadata': data, 'content': pobject.words}}
raise TypeError(repr(pobject) + ' is not JSON serializable')
def from_json(json_object):
if '__class__' in json_object and json_object['__class__'] == 'Catalog':
data = json_object['__value__']
metadata = data['metadata']
result = Catalog(metadata['name'], metadata['comment'])
result.words = data['content']
return result
def main():
catalog = Catalog('Name', 'Kommentar')
print catalog
json_catalog = to_json(catalog)
pprint(json_catalog)
catalog = from_json(json_catalog)
print catalog
if __name__ == '__main__':
main()
Es sieht so aus als wenn Du etwas deserialisieren willst, was nicht mit dieser Version von `to_json()` umgewandelt wurde. Wie sehen denn die JSON-Daten aus?
Ausserdem wären minimale, aber lauffähige Beispiele hilfreich, die das Problem aufzeigen, damit das einfacher nachvollziehen kann, ohne selbst noch viel Code drumherum schreiben zu müssen. Sowie vollständige Tracebacks, statt nur der letzten Zeilen.
Re: JSON wiedereinlesen mit eigenen Objekten
Verfasst: Samstag 11. Februar 2012, 09:47
von BlackJack
@palango: *So* bekomme ich die Ausnahme auch:
Code: Alles auswählen
def main():
catalog = Catalog('Name', 'Kommentar')
print catalog
json_catalog = json.dumps(catalog, indent=2, default=to_json)
print json_catalog
catalog = json.loads(json_catalog, object_hook=from_json)
print catalog
Die `object_hook`-Funktion wird für *jedes* JSON-Objekt aufgerufen. Und Deine gibt bei allen JSON-Objekten, die kein "__class__"-Attribut mit dem Wert "Catalog" haben den Wert `None` zurück, weil das der Wert ist, der implizit von allen Funktionen zurückgegeben wird, wenn der Programmfluss das Funktionsende ohne ein explizites ``return`` erreicht. Lass Dir mal am Anfang der `from_json()`-Funktion das Argument ausgegen um zu sehen mit welchen Argumenten die Funktion in welcher Reihenfolge aufgerufen wird.
Re: JSON wiedereinlesen mit eigenen Objekten
Verfasst: Samstag 11. Februar 2012, 09:51
von mutetella
Hallo,
auf den ersten Blick würde ich sagen, dass das nicht JSON ist, was Deine 'to_json'-Funktion zurückgibt.
Grundsätzlich aber erst einmal die Frage: Warum nutzt Du nicht das json-Modul?
Code: Alles auswählen
In [1]: import json
In [2]: vitamine = {'obst': {'C': ['zitrone', 'orange'], 'A': ['aprikose']},'gemuese': {'C': ['kartoffeln'], 'D': ['karotten']}}
In [3]: saved_vitamine = json.dumps(vitamine)
In [4]: del vitamine
In [5]: vitamine
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-5-da014a0489c1> in <module>()
----> 1 vitamine
NameError: name 'vitamine' is not defined
In [6]: vitamine = json.loads(saved_vitamine)
In [7]: vitamine
Out[7]:
{u'gemuese': {u'C': [u'kartoffeln'], u'D': [u'karotten']},
u'obst': {u'A': [u'aprikose'], u'C': [u'zitrone', u'orange']}}
Oder verkenn' ich das Problem?
mutetella
Re: JSON wiedereinlesen mit eigenen Objekten
Verfasst: Samstag 11. Februar 2012, 09:58
von BlackJack
@mutetella: Schau Dir die `main()` in meinem letzten Beitrag an, wo die beiden Funktion mit dem `json`-Modul verwendet werden, und zu dem Problem aus dem Ursprungsbeitrag führen.
Re: JSON wiedereinlesen mit eigenen Objekten
Verfasst: Samstag 11. Februar 2012, 21:14
von palango
@BlackJack: danke für die Hilfe, ein einfaches
am Ende der Deserialisierung tut seinen Dienst!
@Mutella: Gundproblem ist das ich ein eigenes Objekt serialisieren will, deswegen der ganze Aufwand.
Re: JSON wiedereinlesen mit eigenen Objekten
Verfasst: Samstag 11. Februar 2012, 21:42
von palango
So, nächstes Problem. Ich habe jetzt meine Catalog-Klasse von list abgeleitet um das ganze ein bisschen besser nutzbar zu machen. Der Code inkl. Beispiel sieht jetzt so aus:
Code: Alles auswählen
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, json
from pprint import pprint
class Catalog(list):
def __init__(self, name, comment="", data=[]):
list.__init__(self, data)
self.name = name
self.comment = comment
self.append(["hängen", "colgar", 0])
self.append(["essen", "comer", 0])
self.append(["arbeiten", "trabajar", 0])
self.append(["sitzen", "sentar", 0])
def __repr__(self):
return '<%s name:%r comment:%r words:%s>' % (
self.__class__.__name__, self.name, self.comment, list(self)
)
def to_json(pobject):
print pobject.__class__
if isinstance(pobject, Catalog):
data = {"name": pobject.name, "comment": pobject.comment}
pprint(data)
return {'__class__': "Catalog",
'__value__': {"metadata": data, "content": pobject}}
raise TypeError(repr(pobject) + ' is not JSON serializable')
def from_json(json_object):
if "__class__" in json_object and json_object["__class__"] == "Catalog":
data = json_object["__value__"]
metadata = data["metadata"]
return Catalog(metadata["name"], metadata["comment"], data["content"])
return json_object
with open("json.test", "w") as f:
c = Catalog("test", "blah")
print c
json.dump(c, f, default=to_json, indent=2, encoding="UTF-8")
print "dumped"
with open('json.test', 'r') as f:
entry = json.load(f, object_hook=from_json, encoding="UTF-8")
print entry.name
pprint (entry)
Der Output ist folgender:
Code: Alles auswählen
python catalog.py
<Catalog name:'test' comment:'blah' words:[['h\xc3\xa4ngen', 'colgar', 0], ['essen', 'comer', 0], ['arbeiten', 'trabajar', 0], ['sitzen', 'sentar', 0]]>
dumped
Traceback (most recent call last):
File "catalog.py", line 49, in <module>
print entry.name
AttributeError: 'list' object has no attribute 'name'
Es scheint so, als würde meine Serialisierungsfunktion gar nciht mehr genutzt werden, weil nie die Klasse von pobject ausgegeben wird. Meinungen dazu?
Re: JSON wiedereinlesen mit eigenen Objekten
Verfasst: Sonntag 12. Februar 2012, 00:04
von nomnom
Wird sie auch nicht. Vermutlich erkennt JSONEncoder, dass `Catalog` von `list` abgeleitet wird, und speichert einfach die Liste.
JSONEncoder.__doc__ hat geschrieben:If specified, default is a function that gets called for objects that can't otherwise be serialized.
Ich würde dir einfach dazu raten, einfach to_json(obj) zu dumpen, statt `default` zu nutzen. Außerdem würde ich (to|from)_json als (Klassen-)Methoden implementieren.
Re: JSON wiedereinlesen mit eigenen Objekten
Verfasst: Sonntag 12. Februar 2012, 12:07
von sma
Es sieht übrigens nicht richtig aus, Textdateien mit "r" und "w" zu öffnen, und dann mit Bytestrings (encoding="utf-8") zu arbeiten. Ich würde empfehlen, "rb" und "wb" zu benutzen, um keine Überraschungen zu erleben.
Stefan