python - ¿La forma más sencilla de serializar un objeto de clase simple con simplejson?
modulo shelve python (7)
Como se especifica en JSON docs // help(json.dumps)
//>
Simplemente debe anular el método default()
de JSONEncoder
para proporcionar una conversión de tipos personalizada y pasarla como argumento cls
.
Aquí hay uno que uso para cubrir los tipos de datos especiales de Mongo (datetime y ObjectId)
class MongoEncoder(json.JSONEncoder):
def default(self, v):
types = {
''ObjectId'': lambda v: str(v),
''datetime'': lambda v: v.isoformat()
}
vtype = type(v).__name__
if vtype in types:
return types[type(v).__name__](v)
else:
return json.JSONEncoder.default(self, v)
Llamándolo tan simple como
data = json.dumps(data, cls=MongoEncoder)
Estoy intentando serializar una lista de objetos python con JSON (usando simplejson) y obtengo el error de que el objeto "no es serializable por JSON".
La clase es una clase simple que tiene campos que solo son enteros, cadenas y flotantes, y hereda campos similares de una superclase principal, por ejemplo:
class ParentClass:
def __init__(self, foo):
self.foo = foo
class ChildClass(ParentClass):
def __init__(self, foo, bar):
ParentClass.__init__(self, foo)
self.bar = bar
bar1 = ChildClass(my_foo, my_bar)
bar2 = ChildClass(my_foo, my_bar)
my_list_of_objects = [bar1, bar2]
simplejson.dump(my_list_of_objects, my_filename)
donde foo, bar son tipos simples como he mencionado anteriormente. Lo único difícil es que ChildClass a veces tiene un campo que se refiere a otro objeto (de un tipo que no es ParentClass o ChildClass).
¿Cuál es la forma más fácil de serializar esto como un objeto json con simplejson? ¿Es suficiente hacer que sea serializable como un diccionario? ¿Es la mejor manera de simplemente escribir un método dict para ChildClass? Finalmente, ¿tener el campo que se refiere a otro objeto complica significativamente las cosas? Si es así, puedo reescribir mi código para que solo tenga campos simples en las clases (como cadenas / flotadores, etc.)
gracias.
Esto es una especie de piratería y estoy seguro de que hay muchas cosas que pueden estar equivocadas. Sin embargo, estaba produciendo un script simple y corrí el problema de que no quería crear una subclase de mi serializador json para serializar una lista de objetos modelo. Terminé usando la lista de comprensión
Deje: los activos = lista de objetos de modelo
Código:
myJson = json.dumps([x.__dict__ for x in assets])
Hasta el momento parece haber funcionado con mucho encanto para mis necesidades.
He usado esta estrategia en el pasado y estoy bastante contento con ella: codifique sus objetos personalizados como literales de objetos JSON (como los dict
Python) con la siguiente estructura:
{ ''__ClassName__'': { ... } }
Eso es esencialmente un dict
un solo elemento cuya clave única es una cadena especial que especifica qué tipo de objeto se codifica, y cuyo valor es un dict
de los atributos de la instancia. Si eso tiene sentido.
Una implementación muy simple de un codificador y un decodificador (simplificado a partir del código que realmente he usado) es así:
TYPES = { ''ParentClass'': ParentClass,
''ChildClass'': ChildClass }
class CustomTypeEncoder(json.JSONEncoder):
"""A custom JSONEncoder class that knows how to encode core custom
objects.
Custom objects are encoded as JSON object literals (ie, dicts) with
one key, ''__TypeName__'' where ''TypeName'' is the actual name of the
type to which the object belongs. That single key maps to another
object literal which is just the __dict__ of the object encoded."""
def default(self, obj):
if isinstance(obj, TYPES.values()):
key = ''__%s__'' % obj.__class__.__name__
return { key: obj.__dict__ }
return json.JSONEncoder.default(self, obj)
def CustomTypeDecoder(dct):
if len(dct) == 1:
type_name, value = dct.items()[0]
type_name = type_name.strip(''_'')
if type_name in TYPES:
return TYPES[type_name].from_dict(value)
return dct
En esta implementación, se supone que los objetos que está codificando tendrán un método de clase from_dict()
que sabe cómo recrear una instancia a partir de un dict
descodificado de JSON.
Es fácil expandir el codificador y el decodificador para admitir tipos personalizados (por ejemplo, objetos de datetime
).
EDITAR , para responder a su edición: Lo bueno de una implementación como esta es que codificará y decodificará automáticamente las instancias de cualquier objeto encontrado en el mapeo TYPES
. Eso significa que manejará automáticamente un ChildClass así:
class ChildClass(object):
def __init__(self):
self.foo = ''foo''
self.bar = 1.1
self.parent = ParentClass(1)
Eso debería resultar en JSON algo como lo siguiente:
{ ''__ChildClass__'': {
''bar'': 1.1,
''foo'': ''foo'',
''parent'': {
''__ParentClass__'': {
''foo'': 1}
}
}
}
Me siento un poco tonto con respecto a mis posibles 2 soluciones al releerlo ahora, por supuesto, cuando usa django-rest-framework, este marco tiene algunas características excelentes para este problema mencionado anteriormente.
Ver este ejemplo de vista modelo en su sitio web.
Si no está utilizando django-rest-framework, esto puede ayudar de todos modos:
Encontré 2 soluciones útiles para este problema en esta página: (¡Me gusta la segunda más!)
Posible solución 1 (o camino a seguir): David Chambers Design hizo una buena solución
Espero que a David no le importe que copie y pegue aquí su código de solución:
Defina un método de serialización en el modelo de la instancia:
def toJSON(self):
import simplejson
return simplejson.dumps(dict([(attr, getattr(self, attr)) for attr in [f.name for f in self._meta.fields]]))
e incluso extrajo el método anterior, por lo que es más legible:
def toJSON(self):
fields = []
for field in self._meta.fields:
fields.append(field.name)
d = {}
for attr in fields:
d[attr] = getattr(self, attr)
import simplejson
return simplejson.dumps(d)
Por favor, tenga en cuenta que no es mi solución, todos los créditos van al enlace incluido. Sólo pensé que esto debería estar en el desbordamiento de pila.
Esto también podría implementarse en las respuestas anteriores.
Solución 2:
Mi solución preferible se encuentra en esta página:
http://www.traddicts.org/webdevelopment/flexible-and-simple-json-serialization-for-django/
Por cierto, vi al escritor de esta segunda y mejor solución: también está en :
Selaux
Espero que vea esto, ¿y podemos hablar de comenzar a implementar y mejorar su código en una solución abierta?
Si está utilizando Django, puede hacerlo fácilmente a través del módulo de serializadores de Django. Puede encontrar más información aquí: https://docs.djangoproject.com/en/dev/topics/serialization/
Tengo un problema similar pero la función json.dump
no es llamada por mí. Por lo tanto, para hacer que MyClass
JSON se pueda serializar sin darle un codificador personalizado a json.dump
, debe aplicar el parche de Monkey al codificador json.
Primero crea tu codificador en tu módulo my_module
:
import json
class JSONEncoder(json.JSONEncoder):
"""To make MyClass JSON serializable you have to Monkey patch the json
encoder with the following code:
>>> import json
>>> import my_module
>>> json.JSONEncoder.default = my_module.JSONEncoder.default
"""
def default(self, o):
"""For JSON serialization."""
if isinstance(o, MyClass):
return o.__repr__()
else:
return super(self,o)
class MyClass:
def __repr__(self):
return "my class representation"
Entonces, como se describe en el comentario, el parche mono del codificador json:
import json
import my_module
json.JSONEncoder.default = my_module.JSONEncoder.default
Ahora, incluso una llamada de json.dump
en una biblioteca externa (donde no puede cambiar el parámetro cls
) funcionará para sus objetos my_module.MyClass
.
Una instancia de una clase personalizada podría representarse como una cadena con formato JSON con la ayuda de la siguiente función:
def json_repr(obj):
"""Represent instance of a class as JSON.
Arguments:
obj -- any object
Return:
String that reprent JSON-encoded object.
"""
def serialize(obj):
"""Recursively walk object''s hierarchy."""
if isinstance(obj, (bool, int, long, float, basestring)):
return obj
elif isinstance(obj, dict):
obj = obj.copy()
for key in obj:
obj[key] = serialize(obj[key])
return obj
elif isinstance(obj, list):
return [serialize(item) for item in obj]
elif isinstance(obj, tuple):
return tuple(serialize([item for item in obj]))
elif hasattr(obj, ''__dict__''):
return serialize(obj.__dict__)
else:
return repr(obj) # Don''t know how to handle, convert to string
return json.dumps(serialize(obj))
Esta función producirá una cadena con formato JSON para
una instancia de una clase personalizada,
un diccionario que tiene instancias de clases personalizadas como hojas,
- una lista de instancias de clases personalizadas