Cómo hacer que una clase JSON sea serializable

Resuelto Sergey asked hace 14 años • 45 respuestas

¿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
Sergey avatar Sep 22 '10 18:09 Sergey
Aceptado

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"
}
Onur Yıldırım avatar Mar 21 '2013 02:03 Onur Yıldırım

¿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 JSONEncodere 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 clskwarg:

json.dumps(cls=MyEncoder)

Si también desea decodificar, deberá proporcionar una costumbre object_hooka la JSONDecoderclase. 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>
>>> 
Manoj Govindan avatar Sep 22 '2010 12:09 Manoj Govindan

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)

gecco avatar Dec 23 '2011 09:12 gecco

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.

andyhasit avatar Jul 03 '2015 13:07 andyhasit

Como se menciona en muchas otras respuestas, puede pasar una función para json.dumpsconvertir 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 varspara 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 JSONEncodercomo se describe en las otras respuestas.

user1587520 avatar Oct 21 '2020 18:10 user1587520