serialize query parser crear python json serialization

python - query - Como hacer una clase JSON serializable.



python return json (22)

¿Cómo hacer una clase Python serializable?

Una clase simple:

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

¿Qué debo hacer para poder obtener una salida de:

json.dumps()

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


¿Tienes una idea sobre el resultado esperado? Por ejemplo, ¿hará esto?

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

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

Si desea una salida más personalizada, tendrá que subclasificar JSONEncoder e implementar su propia serialización personalizada.

Para un ejemplo trivial, ver más abajo.

>>> 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 método json.dumps() como cls kwarg:

json.dumps(cls=MyEncoder)

Si también desea descodificar, deberá proporcionar un object_hook personalizado a la clase json.dumps() . 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> >>>


Aquí están mis 3 centavos ...
Esto demuestra la serialización json explícita para un objeto python parecido a un árbol.
Nota: Si realmente quisiera un código como este, podría usar la clase FilePath retorcida .

import json, sys, os class File: def __init__(self, path): self.path = path def isdir(self): return os.path.isdir(self.path) def isfile(self): return os.path.isfile(self.path) def children(self): return [File(os.path.join(self.path, f)) for f in os.listdir(self.path)] def getsize(self): return os.path.getsize(self.path) def getModificationTime(self): return os.path.getmtime(self.path) def _default(o): d = {} d[''path''] = o.path d[''isFile''] = o.isfile() d[''isDir''] = o.isdir() d[''mtime''] = int(o.getModificationTime()) d[''size''] = o.getsize() if o.isfile() else 0 if o.isdir(): d[''children''] = o.children() return d folder = os.path.abspath(''.'') json.dump(File(folder), sys.stdout, default=_default)


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

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

Así que solo lo llamas para serializar:

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

saldrá:

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


Elegí usar decoradores para resolver el problema de serialización de objetos de fecha y hora. Aquí está mi código:

#myjson.py #Author: jmooremcc 7/16/2017 import json from datetime import datetime, date, time, timedelta """ This module uses decorators to serialize date objects using json The filename is myjson.py In another module you simply add the following import statement: from myjson import json json.dumps and json.dump will then correctly serialize datetime and date objects """ def json_serial(obj): """JSON serializer for objects not serializable by default json code""" if isinstance(obj, (datetime, date)): serial = str(obj) return serial raise TypeError ("Type %s not serializable" % type(obj)) def FixDumps(fn): def hook(obj): return fn(obj, default=json_serial) return hook def FixDump(fn): def hook(obj, fp): return fn(obj,fp, default=json_serial) return hook json.dumps=FixDumps(json.dumps) json.dump=FixDump(json.dump) if __name__=="__main__": today=datetime.now() data={''atime'':today, ''greet'':''Hello''} str=json.dumps(data) print str

Al importar el módulo anterior, mis otros módulos usan json de manera normal (sin especificar la palabra clave predeterminada) para serializar los datos que contienen objetos de fecha y hora. El código del serializador de fecha y hora se llama automáticamente para json.dumps y json.dump.


Encontré este problema el otro día e implementé una versión más general de un codificador para objetos de 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" }


Esta clase puede hacer el truco, convierte el objeto a json estándar.

import json class Serializer(object): @staticmethod def serialize(object): return json.dumps(object, default=lambda o: o.__dict__.values()[0])

uso:

Serializer.serialize(my_object)

trabajando en python2.7 y python3 .



Hay muchos enfoques para este problema. ''ObjDict'' (pip install objdict) es otro. Hay un énfasis en proporcionar objetos como javascript que también pueden actuar como diccionarios para manejar mejor los datos cargados desde JSON, pero hay otras características que también pueden ser útiles. Esto proporciona otra solución alternativa al problema original.


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

Si desea poder llamar a json.dumps (obj) 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 básica de datos, para cosas más complicadas, siempre puede establecer claves explícitamente.


Me encontré con este problema cuando intenté almacenar el modelo de Peewee en PostgreSQL JSONField .

Después de luchar por un tiempo, aquí está la solución general.

La clave de mi solución es revisar el código fuente de Python y darse cuenta de que la documentación del código (que se describe here ) ya explica cómo extender los json.dumps existentes para admitir otros tipos de datos.

Supongamos que actualmente tiene un modelo que contiene algunos campos que no son serializables a JSON y el modelo que contiene el campo JSON originalmente tiene este aspecto:

class SomeClass(Model): json_field = JSONField()

Solo define un JSONEncoder personalizado como este:

class CustomJsonEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, SomeTypeUnsupportedByJsonDumps): return < whatever value you want > return json.JSONEncoder.default(self, obj) @staticmethod def json_dumper(obj): return json.dumps(obj, cls=CustomJsonEncoder)

Y luego solo JSONField en su JSONField como a continuación:

class SomeClass(Model): json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)

La clave es el método default(self, obj) anterior. Para cada ... is not JSON serializable queja ... is not JSON serializable que reciba de Python, solo agregue código para manejar el tipo unserializable-to-JSON (como Enum o datetime )

Por ejemplo, así es como apoyo una clase heredada de Enum :

class TransactionType(Enum): CURRENT = 1 STACKED = 2 def default(self, obj): if isinstance(obj, TransactionType): return obj.value return json.JSONEncoder.default(self, obj)

Finalmente, con el código implementado como anteriormente, puedes convertir cualquier modelo de Peewee para que sea un objeto JSON-seriable como a continuación:

peewee_model = WhateverPeeweeModel() new_model = SomeClass() new_model.json_field = model_to_dict(peewee_model)

Aunque el código anterior era (algo) específico de Peewee, pero creo que:

  1. Es aplicable a otros ORMs (Django, etc.) en general
  2. Además, si comprendió cómo funciona json.dumps , esta solución también funciona con Python (sin ORM) en general

Cualquier duda, por favor publicar en la sección de comentarios. ¡Gracias!


Me gustó más el método de Lost Koder. Me encontré con problemas al intentar serializar objetos más complejos cuyos miembros / métodos no son serializables. Aquí está mi implementación que funciona en más objetos:

class Serializer(object): @staticmethod def serialize(obj): def check(o): for k, v in o.__dict__.items(): try: _ = json.dumps(v) o.__dict__[k] = v except TypeError: o.__dict__[k] = str(v) return o return json.dumps(check(obj).__dict__, indent=2)


Me gusta la respuesta de Onur pero me expandiría para incluir un toJSON() opcional 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)


Otra opción es envolver el volcado 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

Pruebas:

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


Para clases más complejas usted podría considerar la herramienta jsonpickle :

jsonpickle es una biblioteca de Python para la serialización y deserialización de objetos Python complejos desde y hacia 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 tienen un equivalente directo de JSON (por ejemplo, dicts, listas, cadenas, intts, etc.). jsonpickle se basa en estas bibliotecas y permite que las estructuras de datos más complejas se serialicen a JSON. jsonpickle es altamente configurable y ampliable, lo que permite al usuario elegir el backend de JSON y agregar backends adicionales.

(jsonpickle en PyPi)


Se me ocurrió mi propia solución. Utilice este método, pase cualquier documento ( dict , lista , ObjectId , etc.) para serializar.

def getSerializable(doc): # check if it''s a list if isinstance(doc, list): for i, val in enumerate(doc): doc[i] = getSerializable(doc[i]) return doc # check if it''s a dict if isinstance(doc, dict): for key in doc.keys(): doc[key] = getSerializable(doc[key]) return doc # Process ObjectId if isinstance(doc, ObjectId): doc = str(doc) return doc # Use any other custom serializting stuff here... # For the rest of stuff return doc


Si no le importa instalar un paquete para él, puede usar json-tricks :

pip install json-tricks

Después de eso, solo necesitas importar los dump(s) de json_tricks lugar de json, y normalmente funcionará:

from json_tricks import dumps json_str = dumps(cls_instance, indent=4)

que daré

{ "__instance_type__": [ "module_name.test_class", "MyTestCls" ], "attributes": { "attr": "val", "dct_attr": { "hello": 42 } } }

¡Y eso es básicamente!

Esto funcionará muy bien en general. Hay algunas excepciones, por ejemplo, si ocurren cosas especiales en __new__ , o si se está produciendo más metaclase mágica.

Obviamente, cargar también funciona (de lo contrario, ¿cuál es el punto)?

from json_tricks import loads json_str = loads(json_str)

Esto asume que module_name.test_class.MyTestCls puede ser importado y no ha cambiado en formas no compatibles. Obtendrá una instancia , no un diccionario o algo así, y debería ser una copia idéntica a la que descargó.

Si desea personalizar la forma en que se desordena (serializa) algo, puede agregar métodos especiales a su clase, como por ejemplo:

class CustomEncodeCls: def __init__(self): self.relevant = 42 self.irrelevant = 37 def __json_encode__(self): # should return primitive, serializable types like dict, list, int, string, float... return {''relevant'': self.relevant} def __json_decode__(self, **attrs): # should initialize all properties; note that __init__ is not called implicitly self.relevant = attrs[''relevant''] self.irrelevant = 12

que serializa solo parte de los parámetros de atributos, como ejemplo.

Y como bonificación gratuita, obtiene (des) serialización de arrays numpy, fecha y hora, mapas ordenados, así como la posibilidad de incluir comentarios en json.

Descargo de responsabilidad: creé json_tricks , porque tuve el mismo problema que tú.


Solo agrega el método to_json a tu clase de esta manera:

def to_json(self): return self.message # or how you want it to be serialized

Y agrega este código (de esta respuesta ) , a algún lugar en la parte superior de todo:

from json import JSONEncoder def _default(self, obj): return getattr(obj.__class__, "to_json", _default.default)(obj) _default.default = JSONEncoder().default JSONEncoder.default = _default

Este será el módulo json de parche de mono cuando se importa, por lo que JSONEncoder.default () busca automáticamente un método especial "to_json ()" y lo utiliza para codificar el objeto si lo encuentra.

Al igual que Onur dijo, pero esta vez no tiene que actualizar cada json.dumps() en su proyecto.


jsonweb parece ser la mejor solución para mí. Ver http://www.jsonweb.info/en/latest/

from jsonweb.encode import to_object, dumper @to_object() class DataModel(object): def __init__(self, id, value): self.id = id self.value = value >>> data = DataModel(5, "foo") >>> dumper(data) ''{"__type__": "DataModel", "id": 5, "value": "foo"}''


jaraco dio una respuesta bastante ordenada. Necesitaba arreglar algunas cosas menores, pero esto funciona:

Código

# Your custom class class MyCustom(object): def __json__(self): return { ''a'': self.a, ''b'': self.b, ''__python__'': ''mymodule.submodule:MyCustom.from_json'', } to_json = __json__ # supported by simplejson @classmethod def from_json(cls, json): obj = cls() obj.a = json[''a''] obj.b = json[''b''] return obj # Dumping and loading import simplejson obj = MyCustom() obj.a = 3 obj.b = 4 json = simplejson.dumps(obj, for_json=True) # Two-step loading obj2_dict = simplejson.loads(json) obj2 = MyCustom.from_json(obj2_dict) # Make sure we have the correct thing assert isinstance(obj2, MyCustom) assert obj2.__dict__ == obj.__dict__

Tenga en cuenta que necesitamos dos pasos para cargar. Por ahora, la propiedad __python__ no se usa.

¿Qué tan común es esto?

Usando el método de AlJohri , AlJohri popularidad de los enfoques:

Serialización (Python -> JSON):

  • to_json : 266,595 en 2018-06-27
  • aJSON: 96,307 en 2018-06-27
  • __json__ : 8,504 en 2018-06-27
  • for_json : 6,937 en 2018-06-27

Deserialización (JSON -> Python):


json está limitado en cuanto a los objetos que puede imprimir, y jsonpickle (puede que necesite un pip install jsonpickle ) está limitado en cuanto a que no puede sangrar texto. Si desea inspeccionar el contenido de un objeto cuya clase no puede cambiar, todavía no puedo encontrar una manera más recta que:

import json import jsonpickle ... print json.dumps(json.loads(jsonpickle.encode(object)), indent=2)

Tenga en cuenta que todavía no pueden imprimir los métodos de objeto.


import json class Foo(object): def __init__(self): self.bar = ''baz'' self._qux = ''flub'' def somemethod(self): pass def default(instance): return {k: v for k, v in vars(instance).items() if not str(k).startswith(''_'')} json_foo = json.dumps(Foo(), default=default) assert ''{"bar": "baz"}'' == json_foo print(json_foo)


import simplejson class User(object): def __init__(self, name, mail): self.name = name self.mail = mail def _asdict(self): return self.__dict__ print(simplejson.dumps(User(''alice'', ''[email protected]'')))

Si usa json estándar, necesita definir una función default

import json def default(o): return o._asdict() print(json.dumps(User(''alice'', ''[email protected]''), default=default))