2011-03-01 30 views
27

Quiero codificar objetos en JSON. Pero no puedo entender cómo hacer que la salida escape sin la cadena.Codificación del objeto python anidado en JSON

import json 

class Abc: 
    def __init__(self): 
     self.name="abc name" 
    def toJSON(self): 
     return json.dumps(self.__dict__, cls=ComplexEncoder) 

class Doc: 
    def __init__(self): 
     self.abc=Abc() 
    def toJSON(self): 
     return json.dumps(self.__dict__, cls=ComplexEncoder) 

class ComplexEncoder(json.JSONEncoder): 
    def default(self, obj): 
     if isinstance(obj, Abc) or isinstance(obj, Doc): 
      return obj.toJSON() 
     else: 
      return json.JSONEncoder.default(self, obj) 

doc=Doc() 
print doc.toJSON() 

El resultado es (los vertederos devuelve una representación de cadena, por eso el "se escaparon)

{"abc": "{\"name\": \"abc name\"}"} 

quiero algo un poco diferente. El resultado esperado es

{"abc": {"name": "abc name"}"} 

Pero no veo cómo ... ¿Alguna pista?

gracias i n avance.

Respuesta

18

Por lo tanto, el problema inmediato es que está pasando el módulo json un valor JSON, que se codificará como una cadena más en el valor JSON.

El problema más amplio es que estás complicando demasiado esto.

Basándose en JSON datetime between Python and JavaScript, me gustaría ir con algo más parecido a esto:

import json 

class Abc: 
    def __init__(self): 
     self.name="abc name" 
    def jsonable(self): 
     return self.name 

class Doc: 
    def __init__(self): 
     self.abc=Abc() 
    def jsonable(self): 
     return self.__dict__ 

def ComplexHandler(Obj): 
    if hasattr(Obj, 'jsonable'): 
     return Obj.jsonable() 
    else: 
     raise TypeError, 'Object of type %s with value of %s is not JSON serializable' % (type(Obj), repr(Obj)) 

doc=Doc() 
print json.dumps(doc, default=ComplexHandler) 

que se obtiene:

~$ python nestjson.py 
{"abc": "abc name"} 
~$ 

Esto puede hacerse limpiador/cuerdo/más seguro (en particular, simplemente acaparar __dict__ generalmente no es una tarea recomendada para realizar la depuración/solución de problemas externos), pero debe aclarar el punto. Todo lo que necesita, fundamentalmente, es una forma de obtener un objeto compatible con json (ya sea una cadena o número simple, o una lista o dict) de cada "nodo" en el árbol. Ese objeto debería ser no ser un objeto ya serializado por JSON, que es lo que estaba haciendo.

29

mi ejemplo anterior, con otro objeto anidado y sus consejos:

import json 

class Identity: 
    def __init__(self): 
     self.name="abc name" 
     self.first="abc first" 
     self.addr=Addr() 
    def reprJSON(self): 
     return dict(name=self.name, firstname=self.first, address=self.addr) 

class Addr: 
    def __init__(self): 
     self.street="sesame street" 
     self.zip="13000" 
    def reprJSON(self): 
     return dict(street=self.street, zip=self.zip) 

class Doc: 
    def __init__(self): 
     self.identity=Identity() 
     self.data="all data" 
    def reprJSON(self): 
     return dict(id=self.identity, data=self.data) 

class ComplexEncoder(json.JSONEncoder): 
    def default(self, obj): 
     if hasattr(obj,'reprJSON'): 
      return obj.reprJSON() 
     else: 
      return json.JSONEncoder.default(self, obj) 

doc=Doc() 
print "Str representation" 
print doc.reprJSON() 
print "Full JSON" 
print json.dumps(doc.reprJSON(), cls=ComplexEncoder) 
print "Partial JSON" 
print json.dumps(doc.identity.addr.reprJSON(), cls=ComplexEncoder) 

produce el resultado esperado:

Str representation 
{'data': 'all data', 'id': <__main__.Identity instance at 0x1005317e8>} 
Full JSON 
{"data": "all data", "id": {"name": "abc name", "firstname": "abc first", "address": {"street": "sesame street", "zip": "13000"}}} 
Partial JSON 
{"street": "sesame street", "zip": "13000"} 

Gracias.

2

No pude agregar esto como comentario y agregar como respuesta. La muestra final de Fred fue útil para mí. Me dijeron que jsonpickle hace esto, pero no pude conseguir que el módulo se instalara y ejecutara correctamente. Así que usé el código aquí. Sin embargo, aunque tenía pequeños cambios, tenía demasiadas variables para agregar manualmente a algunos de los objetos. Así las cosas este pequeño bucle simplificado:

def reprJSON(self): 
    d = dict() 
    for a, v in self.__dict__.items(): 
     if (hasattr(v, "reprJSON")): 
      d[a] = v.reprJSON() 
     else: 
      d[a] = v 
    return d 

Se pueden utilizar en cualquier objeto que tenga una subclase que está demasiado ocupado para codificar la mano. O puede convertirse en un ayudante para todas las clases. Esto también funciona para la presentación JSON completa de matrices de miembros que contienen otras clases (siempre que implementen reprJSON() por supuesto).

+0

Esto fue fantástico y me permitió formar parte de una clase y manejar atributos como datetime de manera diferente. Además, las clases heredadas pueden llamar a la súper definición y aplicar su propio procesamiento a ciertos atributos. –

4

Para evitar la repetición de código como en la respuesta de Fred Laurent sobrecargué el método __iter__() de la siguiente manera. Esto también permite 'jsonize' elementos de lista, datetime y decimal sin dependencias adicionales, solo use dict().

import datetime 
import decimal 


class Jsonable(object): 
    def __iter__(self): 
     for attr, value in self.__dict__.iteritems(): 
      if isinstance(value, datetime.datetime): 
       iso = value.isoformat() 
       yield attr, iso 
      elif isinstance(value, decimal.Decimal): 
       yield attr, str(value) 
      elif(hasattr(value, '__iter__')): 
       if(hasattr(value, 'pop')): 
        a = [] 
        for subval in value: 
         if(hasattr(subval, '__iter__')): 
          a.append(dict(subval)) 
         else: 
          a.append(subval) 
        yield attr, a 
       else: 
        yield attr, dict(value) 
      else: 
       yield attr, value 

class Identity(Jsonable): 
    def __init__(self): 
     self.name="abc name" 
     self.first="abc first" 
     self.addr=Addr() 

class Addr(Jsonable): 
    def __init__(self): 
     self.street="sesame street" 
     self.zip="13000" 

class Doc(Jsonable): 
    def __init__(self): 
     self.identity=Identity() 
     self.data="all data" 


def main(): 
    doc=Doc() 
    print "-Dictionary- \n" 
    print dict(doc) 
    print "\n-JSON- \n" 
    print json.dumps(dict(doc), sort_keys=True, indent=4) 

if __name__ == '__main__': 
    main() 

La salida:

-Dictionary- 

{'data': 'all data', 'identity': {'first': 'abc first', 'addr': {'street': 'sesame street', 'zip': '13000'}, 'name': 'abc name'}} 

-JSON- 

{ 
    "data": "all data", 
    "identity": { 
     "addr": { 
      "street": "sesame street", 
      "zip": "13000" 
     }, 
     "first": "abc first", 
     "name": "abc name" 
    } 
} 

espero que ayude! Gracias