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