Cómo hacer que una clase JSON sea serializable
¿Cómo hacer que una clase de Python sea serializable?
class FileItem:
def __init__(self, fname):
self.fname = fname
Intente serializar a JSON:
>>> import json
>>> x = FileItem('/foo/bar')
>>> json.dumps(x)
TypeError: Object of type 'FileItem' is not JSON serializable
Aquí hay una solución simple para una característica simple:
.toJSON()
Método
En lugar de una clase serializable JSON, implemente un método serializador:
import json
class Object:
def toJSON(self):
return json.dumps(self, default=lambda o: o.__dict__,
sort_keys=True, indent=4)
Entonces simplemente lo llamas para serializar:
me = Object()
me.name = "Onur"
me.age = 35
me.dog = Object()
me.dog.name = "Apollo"
print(me.toJSON())
generará:
{
"age": 35,
"dog": {
"name": "Apollo"
},
"name": "Onur"
}
¿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 json.dumps(f.__dict__)
.
Si desea una salida más personalizada, tendrá que crear subclases JSONEncoder
e implementar su propia serialización personalizada.
Para ver un ejemplo trivial, consulte a continuación.
>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
def default(self, o):
return o.__dict__
>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'
Luego pasas esta clase al json.dumps()
método como cls
kwarg:
json.dumps(cls=MyEncoder)
Si también desea decodificar, deberá proporcionar una costumbre object_hook
a la JSONDecoder
clase. 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, podrías 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, simplejson y demjson de stdlib, solo pueden manejar primitivas de Python que tengan un equivalente JSON directo (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 ampliable, lo que permite al usuario elegir el backend JSON y agregar backends adicionales.
Transforma un objeto en una cadena JSON:
import jsonpickle
json_string = jsonpickle.encode(obj)
Recrea un objeto Python a partir de una cadena JSON:
recreated_obj = jsonpickle.decode(json_string)
(enlace a jsonpickle en PyPi)
La mayoría de las respuestas implican cambiar la llamada a json.dumps() , lo cual no siempre es posible o deseable (puede suceder dentro de un componente del marco, por ejemplo).
Si desea poder llamar a json.dumps(obj) tal como está, entonces una solución simple es heredar de dict :
class FileItem(dict):
def __init__(self, fname):
dict.__init__(self, fname=fname)
f = FileItem('tasks.txt')
json.dumps(f) #No need to change anything here
Esto funciona si su clase es solo una representación de datos básica; para cosas más complicadas, siempre puede configurar claves explícitamente en la llamada a dict.__init__()
.
Esto funciona porque json.dumps()
verifica si el objeto es uno de varios tipos conocidos a través de un método bastante no pitónico isinstance(value, dict)
, por lo que sería posible modificar esto __class__
y algunos otros métodos si realmente no desea heredar de dict
.
Como se menciona en muchas otras respuestas, puede pasar una función para json.dumps
convertir objetos que no son uno de los tipos admitidos de forma predeterminada en un tipo admitido. Sorprendentemente ninguno de ellos menciona el caso más simple, que es utilizar la función incorporada vars
para convertir objetos en un dict que contenga todos sus atributos:
json.dumps(obj, default=vars)
Tenga en cuenta que esto cubre solo casos básicos; si necesita una serialización más específica para ciertos tipos (por ejemplo, excluir ciertos atributos o para objetos que no tienen un __dict__
atributo), debe usar una función personalizada o JSONEncoder
como se describe en las otras respuestas.