Pregunta Cómo hacer una clase JSON serializable


¿Cómo hacer una clase de Python serializable?

Una clase simple:

class FileItem:
    def __init__(self, fname):
        self.fname = fname

¿Qué debo hacer para obtener resultados de:

json.dumps()

Sin un error (FileItem instance at ... is not JSON serializable)


510
2017-09-22 11:52


origen


Respuestas:


¿Tiene una idea sobre la salida esperada? Por ej. ¿Con esto bastará?

>>> f  = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'

En ese caso, simplemente puede llamar json.dumps(f.__dict__).

Si quieres una salida más personalizada, entonces tendrás que subclasificar JSONEncoder e implementa tu propia serialización personalizada.

Para un ejemplo trivial, mira 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 quieres decodificar, tendrás que proporcionar un servicio personalizado object_hook al JSONDecoder clase. Por ej.

>>> 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>
>>> 

392
2017-09-22 12:02



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 de serialización:

import json

class Object:
    def toJSON(self):
        return json.dumps(self, default=lambda o: o.__dict__, 
            sort_keys=True, indent=4)

Entonces, solo llámalo para serializar:

me = Object()
me.name = "Onur"
me.age = 35
me.dog = Object()
me.dog.name = "Apollo"

print(me.toJSON())

dará salida:

{
    "age": 35,
    "dog": {
        "name": "Apollo"
    },
    "name": "Onur"
}

434
2018-03-21 02:26



Para clases más complejas, podrías considerar la herramienta jsonpickle:

jsonpickle es una biblioteca de Python para serialización y deserialización de objetos complejos de Python desde y hacia JSON.

Las bibliotecas estándar de Python para codificar Python en JSON, como stdlib's json, simplejson y demjson, solo pueden manejar primitivas de Python que tengan un equivalente directo de JSON (por ejemplo, dicts, lists, strings, ints, etc.). jsonpickle se basa en estas bibliotecas y permite serializar estructuras de datos más complejas a JSON. jsonpickle es altamente configurable y extensible, lo que permite al usuario elegir el back-end JSON y agregar backends adicionales.

(jsonpickle en PyPi)


105
2017-12-23 09:11



La mayoría de las respuestas implican cambiar la llamada a json.dumps (), que no siempre es posible o deseable (puede suceder dentro de un componente de marco, por ejemplo).

Si quieres poder llamar json.dumps (obj) como está, entonces una solución simple hereda 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 complejas siempre puede establecer claves de manera explícita.


46
2017-07-03 13:22



Otra opción es eliminar el dumping 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, subclassing clase FileItem de una JsonSerializable clase:

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

Pruebas:

>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'

30
2018-06-16 10:30



me gusta La respuesta de Onur pero se expandiría para incluir un opcional toJSON() método para que los objetos se serialicen a sí mismos:

def dumper(obj):
    try:
        return obj.toJSON()
    except:
        return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)

26
2018-01-27 16:04



El otro día me encontré con este problema e implementé una versión más general de un Encoder para objetos Python que puede manejar objetos anidados y campos heredados:

import json
import inspect

class ObjectEncoder(json.JSONEncoder):
    def default(self, obj):
        if hasattr(obj, "to_json"):
            return self.default(obj.to_json())
        elif hasattr(obj, "__dict__"):
            d = dict(
                (key, value)
                for key, value in inspect.getmembers(obj)
                if not key.startswith("__")
                and not inspect.isabstract(value)
                and not inspect.isbuiltin(value)
                and not inspect.isfunction(value)
                and not inspect.isgenerator(value)
                and not inspect.isgeneratorfunction(value)
                and not inspect.ismethod(value)
                and not inspect.ismethoddescriptor(value)
                and not inspect.isroutine(value)
            )
            return self.default(d)
        return obj

Ejemplo:

class C(object):
    c = "NO"
    def to_json(self):
        return {"c": "YES"}

class B(object):
    b = "B"
    i = "I"
    def __init__(self, y):
        self.y = y

    def f(self):
        print "f"

class A(B):
    a = "A"
    def __init__(self):
        self.b = [{"ab": B("y")}]
        self.c = C()

print json.dumps(A(), cls=ObjectEncoder, indent=2, sort_keys=True)

Resultado:

{
  "a": "A", 
  "b": [
    {
      "ab": {
        "b": "B", 
        "i": "I", 
        "y": "y"
      }
    }
  ], 
  "c": {
    "c": "YES"
  }, 
  "i": "I"
}

21
2018-02-18 14:10