Come rendere serializzabile una classe Python?
Una classe semplice:
class FileItem:
def __init__(self, fname):
self.fname = fname
Cosa devo fare per poter ottenere l'output di:
>>> import json
>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable
Senza l'errore
Avete un'idea del risultato atteso? Per esempio, questo andrà bene?
>>> f = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'
In questo caso potete semplicemente chiamare json.dumps(f.__dict__)
.
Se vuoi un output più personalizzato allora dovrai sottoclasse JSONEncoder
e implementare la tua serializzazione personalizzata.
Per un esempio banale, vedi sotto.
>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
def default(self, o):
return o.__dict__
>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'
Poi si passa questa classe nel metodo json.dumps()
come kwarg cls
:
json.dumps(cls=MyEncoder)
Se vuoi anche decodificare allora dovrai fornire un object_hook
personalizzato alla classe JSONDecoder
. Per es.
>>> 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>
>>>
Per classi più complesse potreste considerare lo strumento jsonpickle:
jsonpickle è una libreria Python per la serializzazione e deserializzazione di oggetti Python complessi da e verso JSON.
Le librerie Python standard per la codifica di Python in JSON, come json, simplejson e demjson della stdlib, possono gestire solo primitive Python che hanno un equivalente diretto in JSON (ad esempio dicts, liste, stringhe, ints, ecc.). jsonpickle si basa su queste librerie e permette a strutture di dati più complesse di essere serializzate in JSON. jsonpickle è altamente configurabile ed estendibile, permettendo all'utente di scegliere il backend JSON e aggiungere ulteriori backend.
Un'altra opzione è quella di avvolgere il dumping JSON nella propria classe:
import json
class FileItem:
def __init__(self, fname):
self.fname = fname
def __repr__(self):
return json.dumps(self.__dict__)
O, ancora meglio, subclassificare la classe FileItem da una classe JsonSerializable
:
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
Test:
>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'