¿Cómo hacer que una clase de Python sea serializable?
Una clase simple:
class FileItem:
def __init__(self, fname):
self.fname = fname
¿Qué debo hacer para poder obtener la salida de:
>>> import json
>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable
Sin el error
¿Tiene una idea sobre el resultado esperado? Por ejemplo, ¿esto servirá?
>>> f = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'
En ese caso puedes simplemente llamar a json.dumps(f.__dict__)
.
Si quieres una salida más personalizada, tendrás que subclasificar JSONEncoder
e implementar tu propia serialización personalizada.
Para un ejemplo trivial, vea abajo.
>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
def default(self, o):
return o.__dict__
>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'
Luego se pasa esta clase al método json.dumps()
como kwarg cls
:
json.dumps(cls=MyEncoder)
Si también quieres decodificar, tendrás que proporcionar un object_hook
personalizado a la clase JSONDecoder
. Por ejemplo
>>> 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>
>>>
Para clases más complejas puedes considerar la herramienta jsonpickle:
jsonpickle es una biblioteca de Python para la serialización y deserialización de objetos complejos de Python hacia y desde JSON.
Las bibliotecas estándar de Python para codificar Python en JSON, como json de stdlib, simplejson y demjson, sólo pueden manejar primitivas de Python que tienen un equivalente directo en JSON (por ejemplo, dicts, listas, cadenas, ints, etc.). jsonpickle se basa en estas bibliotecas y permite serializar estructuras de datos más complejas en JSON. jsonpickle es altamente configurable y extensible, permitiendo al usuario elegir el backend de JSON y añadir backends adicionales.
Otra opción es envolver el volcado de JSON en su propia clase:
import json
class FileItem:
def __init__(self, fname):
self.fname = fname
def __repr__(self):
return json.dumps(self.__dict__)
O, mejor aún, subclasificar la clase FileItem de una clase 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
Probando:
>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'