2010-02-26 8 views
24

Estoy tratando de serializar una lista de objetos python con JSON (usando simplejson) y obtengo el error de que el objeto "no es serializable por JSON".¿La manera más fácil de serializar un objeto de clase simple con simplejson?

La clase es una clase simple que tiene campos que son sólo números enteros, cadenas, y flota, y hereda los campos similares de una superclase de los padres, 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 encima. Lo único delicado 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 dict método 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/carrozas, etc.)

gracias.

+0

http: // stackoverflow. com/questions/2249792/json-serializing-django-models-with-simplejson – voyager

+1

posible duplicado de [Python: cómo hacer una clase JSON serializable] (http://stackoverflow.com/questions/3768895/python-how-to -hacer-una-clase-json-serializable) –

Respuesta

25

he utilizado esta estrategia en el pasado y ha habido muy contento con él: Codificar los objetos personalizados como literales de objetos JSON (como Python dict s) con la siguiente estructura:

{ '__ClassName__': { ... } } 

Eso es esencialmente un uno -item dict cuya única clave es una cadena especial que especifica qué tipo de objeto está codificado y cuyo valor es dict de los atributos de la instancia. Si eso tiene sentido.

Una muy simple implementación de un codificador y un decodificador (simplificado del código que he usado en realidad) 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 asume que los objetos que estés codifican tendrán un from_dict() método de clase que sabe cómo volver a crear una instancia desde un dict decodificado desde JSON.

Es fácil ampliar el codificador y el decodificador para admitir tipos personalizados (por ejemplo, objetos datetime).

EDITAR, para responder a su edición: Lo bueno de una implementación de este tipo es que codificará automáticamente y descodificar las instancias de cualquier objeto que se encuentra en la TYPES mapeo.Eso significa que va a manejar automáticamente un ChildClass así:

class ChildClass(object): 
    def __init__(self): 
     self.foo = 'foo' 
     self.bar = 1.1 
     self.parent = ParentClass(1) 

que debe dar lugar a JSON algo como lo siguiente:

{ '__ChildClass__': { 
    'bar': 1.1, 
    'foo': 'foo', 
    'parent': { 
     '__ParentClass__': { 
      'foo': 1} 
     } 
    } 
} 
+0

¿Cómo se compara esto con usar algo como el módulo jsonpickle? Gracias. – user248237dfsf

+0

Acabo de codificar algo muy similar utilizando el método __ClassName__ en el nuevo módulo Underverse. También puede auto detectar tipos para codificar comprobando si están en el tipo de TIPOS, si no puede agregarlos y codificar sobre la marcha. Todo lo que tiene que hacer entonces es agregar una lista de clases al decodificar el JSON y la codificación se maneja automáticamente. – max

8

Una instancia de una costumbre clase puede ser representada como formato JSON cadena con ayuda del 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á cadena con formato JSON para

  • una instancia de una clase personalizada,

  • un diccionario que tiene instancias de clases personalizados como hojas,

  • una lista de instancias de encargo clases
0

me siento un poco tonta sobre mis posibles 2 soluciones releer ahora, , por supuesto, cuando se utiliza Django-resto-marco, este marco tiene algunas características excelentes edifi para este problema antes mencionado .

ver this model view example en su página web

Si no se está usando Django-resto-marco, esto puede ayudar de todos modos:

me encontré con 2 soluciones para este problema atenta en esta página: (Me gusta el segundo, el más)

Posible solución 1 (o manera de ir): David Chambers Design made a nice solution

espero que David no le importa que copiar y pegar el código de la solución aquí:

definir 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 se extrae el método anterior, por lo que es más fácil de leer:

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) 

favor mente, no es mi solución, toda la los créditos van al enlace incluido. Solo pensé que esto debería estar en el desbordamiento de la pila.

Esto podría implementarse también 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 el escritor de esta segunda y la mejor solución: está en stackoverflow así:

Selaux

espero que ve esto, y podemos hablar acerca de cómo iniciar a i aplicar y mejorar su código en una solución abierta?

2

Según se especifica en la documentación del pitón JSON // // help(json.dumps)>

Simplemente debe reemplazar el método de JSONEncoderdefault() con el fin de proporcionar una conversión de tipo personalizado, y pasarlo como argumento cls.

Aquí está uno que utilizo para cubrir tipos de Mongo especiales de datos (fecha y hora 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) 
0

Esta es una especie de hacker y estoy seguro de que hay probablemente muchas cosas que pueden estar equivocadas con eso. Sin embargo, estaba produciendo un script simple y ejecuté el problema de que no quería subclasificar mi serializador json para serializar una lista de objetos modelo. Terminé usando listas por comprensión

Let: activos = lista de modelobjects

Código:

myJson = json.dumps([x.__dict__ for x in assets]) 

Hasta ahora parece haber funcionado con encanto para mis necesidades

Cuestiones relacionadas