web-dev-qa-db-fra.com

Encodage imbriqué python dans JSON

Je veux encoder des objets en JSON. Mais, je ne peux pas comprendre comment faire la sortie sans que la chaîne 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 (les vidages renvoient une représentation sous forme de chaîne, c'est pourquoi les "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 ... Un indice?

merci d'avance.

33
Fred Laurent

mon échantillon précédent, avec un autre objet imbriqué 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.

40
Fred Laurent

Donc, le problème immédiat est que vous transmettez au module json une valeur JSON, qui sera codée comme une autre chaîne dans la valeur JSON.

Le problème plus large est que vous compliquez considérablement cela.

En s'appuyant sur datetime JSON entre Python et JavaScript , j'irais avec quelque chose de plus proche:

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)

ce qui vous donne:

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

Cela peut être rendu plus propre/plus sain/plus sûr (en particulier, il suffit de saisir __dict__ n'est généralement pas recommandé en dehors du débogage/dépannage), mais il devrait faire passer le message. Tout ce dont vous avez besoin, fondamentalement, est un moyen d'obtenir un objet compatible json (que ce soit une simple chaîne ou un nombre, ou une liste ou un dict) de chaque "nœud" dans l'arborescence. Cet objet ne doit pas être un objet déjà sérialisé en JSON, ce que vous faisiez.

22
Nicholas Knight

Pour éviter la répétition de 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 la liste, datetime et décimal sans dépendances supplémentaires, utilisez simplement 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()

Le résultat:

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

J'espère que cela aide! Merci

6
JeanPaulDepraz

Je ne pourrais pas ajouter ceci comme commentaire et ajouter comme réponse. L'échantillon final de Fred m'a été utile. On m'a dit que jsonpickle le faisait, mais je n'ai pas pu installer et exécuter correctement le module. J'ai donc utilisé le code ici. Tweak mineur cependant, j'avais beaucoup trop de variables à ajouter à la main à certains des objets. Donc, cette petite boucle a simplifié 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 occupée pour être codée à la main. Ou peut être fait un assistant pour toutes les classes. Cela fonctionne également pour la présentation JSON complète des tableaux membres qui contiennent d'autres classes (tant qu'ils implémentent reprJSON () bien sûr).

2
ferhan

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

Il fait une sérialisation imbriquée Python objets et peut facilement être étendu pour sérialiser des types personnalisés.

0
veegee