¿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 resultados de:
>>> import json
>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable
Sin el error
python
json
serialization
Sergey
fuente
fuente
import jsons
vea la respuesta a continuación - funciona perfectamente bienRespuestas:
¿Tienes una idea sobre el resultado esperado? Por ejemplo, ¿hará esto?
En ese caso, simplemente puede llamar
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 abajo.
Luego pasa esta clase al
json.dumps()
método comocls
kwarg:Si también desea decodificar, deberá proporcionar una costumbre
object_hook
a laJSONDecoder
clase. Por ej.fuente
__dict__
no funcionará en todos los casos. Si los atributos no se han establecido después de la instancia del objeto, es__dict__
posible que no se completen por completo. En el ejemplo anterior, está bien, pero si tiene atributos de clase que también desea codificar, no se enumerarán a__dict__
menos que se hayan modificado en la__init__
llamada de la clase o de alguna otra manera después de que se haya instanciado el objeto.from_json()
función utilizada como enlace de objeto debe tener unaelse: return json_object
declaración, por lo que también puede tratar con objetos generales.__dict__
tampoco funciona si lo usa__slots__
en una nueva clase de estilo.JSONEncoder
como la anterior para crear un protocolo personalizado, como verificar la existencia del__json_serializable__
método y llamarlo para obtener una representación serializable JSON del objeto. Esto estaría en consonancia con otros patrones de Python, como__getitem__
,__str__
,__eq__
, y__len__
.__dict__
tampoco funcionará recursivamente, por ejemplo, si un atributo de su objeto es otro objeto.Aquí hay una solución simple para una característica simple:
.toJSON()
MétodoEn lugar de una clase serializable JSON, implemente un método de serializador:
Entonces solo lo llamas para serializar:
dará salida:
fuente
o.__dict___
. Pruebe su propio ejemplo:class MyObject(): def __init__(self): self.prop = 1 j = json.dumps({ "foo": "bar", "baz": MyObject() }, default=lambda o: o.__dict__)
a.__dict__
/b.__dict__
.datetime.datetime
instancias. Lanza el siguiente error:'datetime.datetime' object has no attribute '__dict__'
Para clases más complejas, puede considerar la herramienta jsonpickle :
(enlace a jsonpickle en PyPi)
fuente
jsonpickle
objeto. Además, esto no fue capaz de decodificar un dict de dictos que contienen marcos de datos de pandas.obj = jsonpickle.decode(file.read())
yfile.write(jsonpickle.encode(obj))
.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 de marco).
Si desea poder llamar a json.dumps (obj) como está, entonces una solución simple es heredar de dict :
Esto funciona si su clase es solo una representación de datos básica, para cosas más difíciles siempre puede establecer claves explícitamente.
fuente
dumps
no es una buena solución. Por cierto, en la mayoría de los casos, probablemente desee tenerdict
herencia junto con delegación, lo que significa que tendrá algúndict
atributo de tipo dentro de su clase, luego pasará este atributo como parámetro como inicializaciónsuper().__init__(self.elements)
.Me gusta la respuesta de Onur, pero se expandiría para incluir un
toJSON()
método opcional para que los objetos se serialicen:fuente
json.dumps
manejo personalizado existente e introducirlo. ¡Gracias!try-catch
probablemente haría algo comoif 'toJSON' in obj.__attrs__():
... para evitar una falla silenciosa (en caso de falla en to JSON () por alguna otra razón que no esté allí) ... una falla que potencialmente conduce a la corrupción de datos.Otra opción es envolver el volcado JSON en su propia clase:
O, mejor aún, subclasificar la clase FileItem de una
JsonSerializable
clase:Pruebas:
fuente
__json__encode__
/__json_decode__
(divulgación: hice el último).Simplemente agregue un
to_json
método a su clase como este:Y agregue este código (de esta respuesta ) , a algún lugar en la parte superior de todo:
Esto hará un parche en el módulo json cuando se importe, por lo que JSONEncoder.default () automáticamente busca un método especial "to_json ()" y lo usa para codificar el objeto si se encuentra.
Tal como dijo Onur, pero esta vez no tiene que actualizar todos los elementos
json.dumps()
de su proyecto.fuente
TheObject.to_json = my_serializer
.Me encontré con este problema el otro día e implementé una versión más general de un codificador para objetos Python que puede manejar objetos anidados y campos heredados :
Ejemplo:
Resultado:
fuente
return obj
en la última línea, hice estoreturn super(ObjectEncoder, self).default(obj)
. Referencia AQUÍSi está usando Python3.5 +, podría usarlo
jsons
. Convertirá su objeto (y todos sus atributos de forma recursiva) en un dict.O si querías una cuerda:
O si su clase implementó
jsons.JsonSerializable
:fuente
jsons
biblioteca con clases de datos . ¡Hasta ahora todo bien para mí!si usa estándar
json
, necesita definir unadefault
funciónfuente
json.dumps(User('alice', '[email protected]'), default=lambda x: x.__dict__)
json
está limitado en términos de objetos que puede imprimir yjsonpickle
(es posible que necesite apip install jsonpickle
) está limitado en términos de que no puede sangrar texto. Si desea inspeccionar el contenido de un objeto cuya clase no puede cambiar, todavía no podría encontrar una forma más directa que:Nota: que todavía no pueden imprimir los métodos del objeto.
fuente
Esta clase puede hacer el truco, convierte objetos a json estándar.
uso:
trabajando en
python2.7
ypython3
.fuente
fuente
default(obj)
es una función que debería devolver una versión serializable de obj o elevar TypeError. El valor predeterminadodefault
simplemente genera TypeError.jaraco dio una respuesta bastante ordenada. Necesitaba arreglar algunas cosas menores, pero esto funciona:
Código
Tenga en cuenta que necesitamos dos pasos para cargar. Por ahora, la
__python__
propiedad no se utiliza.¿Qué tan común es esto?
Usando el método de AlJohri , verifico la popularidad de los enfoques:
Serialización (Python -> JSON):
to_json
: 266,595 el 27/06/2018toJSON
: 96,307 el 27/06/2018__json__
: 8,504 el 27/06/2018for_json
: 6,937 el 27/06/2018Deserialización (JSON -> Python):
from_json
: 226,101 el 27/06/2018fuente
Esto ha funcionado bien para mí:
y entonces
y
fuente
Si no le importa instalar un paquete, puede usar json-tricks :
Después de eso, solo necesita importar
dump(s)
desde enjson_tricks
lugar de json, y generalmente funcionará:que dará
¡Y eso es básicamente todo!
Esto funcionará muy bien en general. Hay algunas excepciones, por ejemplo, si suceden cosas especiales
__new__
o si hay más magia de metaclase.Obviamente, la carga también funciona (de lo contrario, cuál es el punto):
Esto supone que
module_name.test_class.MyTestCls
se puede importar y no ha cambiado de manera no compatible. Volverás a una instancia , no un diccionario o algo así, y debería ser una copia idéntica a la que dejó.Si desea personalizar cómo se (des) serializa algo, puede agregar métodos especiales a su clase, así:
que serializa solo parte de los parámetros de los atributos, como un ejemplo.
Y como un bono gratuito, obtienes (des) serialización de matrices numpy, fecha y hora, mapas ordenados, así como la capacidad de incluir comentarios en json.
Descargo de responsabilidad: creé json_tricks , porque tuve el mismo problema que tú.
fuente
jsonweb parece ser la mejor solución para mí. Ver http://www.jsonweb.info/en/latest/
fuente
Aquí están mis 3 centavos ...
Esto demuestra la serialización json explícita para un objeto python con forma de árbol.
Nota: Si realmente quisiera un código como este, podría usar la clase retorcida FilePath .
fuente
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 (descrita aquí ) ya explica cómo extender el código existente.
json.dumps
para admitir otros tipos de datos.Suponga que actualmente tiene un modelo que contiene algunos campos que no son serializables para JSON y el modelo que contiene el campo JSON originalmente se ve así:
Simplemente defina una costumbre
JSONEncoder
como esta:Y luego úsalo en tu me
JSONField
gusta a continuación:La clave es el
default(self, obj)
método anterior. Por cada... is not JSON serializable
queja que reciba de Python, simplemente agregue código para manejar el tipo no serializable a JSON (comoEnum
odatetime
)Por ejemplo, así es como apoyo una clase que hereda de
Enum
:Finalmente, con el código implementado como el anterior, puede convertir cualquier modelo de Peewee para que sea un objeto JSON-seriozable como el siguiente:
Aunque el código anterior era (algo) específico para Peewee, pero creo que:
json.dumps
funciona, esta solución también funciona con Python (sin ORM) en general.Cualquier pregunta, publíquela en la sección de comentarios. ¡Gracias!
fuente
Esta función usa la recursión para iterar sobre cada parte del diccionario y luego llama a los métodos repr () de clases que no son tipos incorporados.
fuente
Esta es una pequeña biblioteca que serializa un objeto con todos sus hijos a JSON y también lo analiza de nuevo:
https://github.com/Toubs/PyJSONSerialization/
fuente
Se me ocurrió mi propia solución. Use este método, pase cualquier documento ( dict , list , ObjectId , etc.) para serializar.
fuente
Elegí usar decoradores para resolver el problema de serialización de objetos de fecha y hora. Aquí está mi código:
Al importar el módulo anterior, mis otros módulos usan json de manera normal (sin especificar la palabra clave predeterminada) para serializar 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.
fuente
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:
fuente
Si puede instalar un paquete, le recomiendo probar el eneldo , que funcionó bien para mi proyecto. Lo bueno de este paquete es que tiene la misma interfaz que
pickle
, por lo que si ya lo ha estado utilizandopickle
en su proyecto, simplemente puede sustituirlo pordill
y ver si se ejecuta el script, sin cambiar ningún código. ¡Entonces es una solución muy barata para probar!(Anti-divulgación completa: de ninguna manera estoy afiliado y nunca he contribuido al proyecto de eneldo).
Instala el paquete:
Luego edite su código para importar en
dill
lugar depickle
:Ejecute su script y vea si funciona. (Si lo hace, es posible que desee limpiar su código para que ya no esté sombreando el
pickle
nombre módulo!)Algunos detalles sobre los tipos de datos que
dill
pueden y no pueden serializarse, desde la página del proyecto :fuente
No veo ninguna mención aquí del control de versiones en serie o backcompat, por lo que publicaré mi solución que he estado usando durante un tiempo. Probablemente tengo mucho más para aprender, específicamente Java y Javascript son probablemente más maduros que yo aquí, pero aquí va
https://gist.github.com/andy-d/b7878d0044a4242c0498ed6d67fd50fe
fuente
Para agregar otra opción: puede usar el
attrs
paquete y elasdict
método.y para volver a convertir
la clase se ve así
fuente
Además de la respuesta de Onur , es posible que desee tratar con el tipo de fecha y hora como se muestra a continuación.
(para manejar: el objeto 'datetime.datetime' no tiene excepción de atributo ' dict ').
Uso:
fuente
Primero necesitamos hacer que nuestro objeto sea compatible con JSON, para que podamos volcarlo usando el módulo JSON estándar. Lo hice de esta manera:
fuente
Sobre la base de la respuesta de Quinten Cabo :
Las diferencias son
list
ytuple
(funciona para matrices NumPy, etc.)__dict__
).float
y,None
por lo tanto, no se convierten en cadenas.Lo que queda como ejercicio para el lector es manejar
__slots__
, clases que son iterables y tienen miembros, clases que son diccionarios y también tienen miembros, etc.fuente