Hoe maak je een Python klasse seriëerbaar?
Een eenvoudige klasse:
class FileItem:
def __init__(self, fname):
self.fname = fname
Wat moet ik doen om output te kunnen krijgen van:
>>> import json
>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable
Zonder de foutmelding
Heb je een idee over de verwachte output? Bijv. is dit voldoende?
>>> f = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'
In dat geval kun je volstaan met het aanroepen van json.dumps(f.__dict__)
.
Als je meer aangepaste uitvoer wilt, dan zul je JSONEncoder
moeten subklassen en je eigen aangepaste serialisatie moeten implementeren.
Voor een triviaal voorbeeld, zie hieronder.
>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
def default(self, o):
return o.__dict__
>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'
Vervolgens geef je deze klasse door in de json.dumps()
methode als cls
kwarg:
json.dumps(cls=MyEncoder)
Als je ook wilt decoderen dan moet je'een custom object_hook
meegeven aan de JSONDecoder
klasse. Voor bijv.
>>> def from_json(json_object):
if 'fname' in json_object:
return FileItem(json_object['fname'])
>>> f = JSONDecoder(object_hook = from_json).decode('{"fname": "/foo/bar"}')
>>> f
<__main__.FileItem object at 0x9337fac>
>>>
Voor complexere klassen zou je de tool jsonpickle kunnen overwegen:
jsonpickle is een Python bibliotheek voor serialisatie en deserialisatie van complexe Python objecten naar en van JSON.
De standaard Python bibliotheken voor het coderen van Python naar JSON, zoals stdlib's json, simplejson, en demjson, kunnen enkel Python primitieven aan die een direct JSON equivalent hebben (b.v. dicts, lijsten, strings, ints, enz.). jsonpickle bouwt verder op deze bibliotheken en maakt het mogelijk om meer complexe datastructuren te serializeren naar JSON. jsonpickle is zeer configureerbaar en uitbreidbaar-waardoor de gebruiker zelf de JSON backend kan kiezen en bijkomende backends kan toevoegen.
Een andere optie is om JSON dumping in te pakken in een eigen klasse:
import json
class FileItem:
def __init__(self, fname):
self.fname = fname
def __repr__(self):
return json.dumps(self.__dict__)
Of, nog beter, de FileItem klasse te subklassen vanuit een JsonSerializable
klasse:
import json
class JsonSerializable(object):
def toJson(self):
return json.dumps(self.__dict__)
def __repr__(self):
return self.toJson()
class FileItem(JsonSerializable):
def __init__(self, fname):
self.fname = fname
Testen:
>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'