Encodage d'un objet Python imbriqué dans JSON

je veux encoder des objets dans JSON. Mais, je ne peux pas comprendre comment faire la sortie sans que la chaîne s'échappe.

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()

le résultat est (le dumps renvoie une représentation de chaîne, c'est pourquoi le "sont échappés)

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

je veux quelque chose d'un peu différent. Le résultat attendu est

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

Mais je ne vois pas comment... Tout soupçon ?

merci d'avance.

31
demandé sur igaurav 2011-03-01 23:40:19

5 réponses

mon échantillon précédent, avec un autre objet emboîté et vos conseils:

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)

produit le résultat attendu:

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"}

Merci.

32
répondu Fred Laurent 2011-03-02 09:14:07

donc, le problème immédiat est que vous passez le module json avec une valeur JSON, qui sera encodée comme une autre chaîne dans la valeur JSON.

le problème plus général est que vous exagérez grandement cela.

en se basant sur JSON datetime entre Python et JavaScript , je dirais quelque chose de plus proche de ceci:

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)

qui vous permet de vous:

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

Cela peut être rendu plus propre/plus propre/plus sûr (en particulier, il n'est généralement pas recommandé de simplement saisir __dict__ en dehors du débogage / Dépannage), mais cela devrait faire passer le message. Tout ce dont vous avez besoin, fondamentalement, c'est d'un moyen de sortir un objet JSON-compatible (que ce soit une simple chaîne ou un nombre, ou une liste ou un dict) de chaque "noeud" dans l'arbre. Cet objet devrait et non être un objet déjà-JSON-sérialisé, ce qui est ce que vous faisiez.

20
répondu Nicholas Knight 2017-05-23 11:54:28

pour éviter la répétition du code comme dans la réponse de Fred Laurent j'ai surchargé la méthode __iter__() comme suit. Cela permet également de "jsoniser" les éléments de liste, datetime et décimal sans dépendances supplémentaires, il suffit d'utiliser 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 sortie:

-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"
    }
}

Espère que cela aide! Merci

5
répondu JeanPaulDepraz 2015-02-25 13:15:29

je ne pouvais pas ajouter un commentaire et d'ajouter comme réponse. L'échantillon final de Fred m'a été utile.On m'a dit que jsonpickle s'en chargeait, mais je n'ai pas pu faire installer et exécuter correctement le module. Donc utilisé le code ici. Mineur tweak bien, j'ai eu beaucoup trop de variables pour ajouter à la main pour certains objets. Ainsi, cette petite boucle simplifie les choses:

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

il peut être utilisé dans tout objet dont la sous-classe est trop chargée pour être encodée à la main. Ou il peut être une aide pour toutes les classes. Cela fonctionne aussi pour la présentation complète JSON des tableaux de membres qui contiennent d'autres classes (aussi longtemps qu'ils implémentent reprJSON() bien sûr).

2
répondu ferhan 2012-04-20 23:28:59

C'est ce que vous cherchez: https://github.com/jsonpickle/jsonpickle

il fait serialization imbriquée d'objets Python et peut facilement être étendu à serialize custom types.

1
répondu veegee 2014-09-19 00:40:09