Кодирование вложенного объекта Python в JSON - PullRequest
33 голосов
/ 01 марта 2011

Я хочу кодировать объекты в 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()

В результате (dumps возвращает строковое представление, поэтому "экранируются")

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

Я хочу что-то немного другое. Ожидаемый результат:

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

Но я не понимаю, как ... Любой намек?

заранее спасибо.

Ответы [ 5 ]

40 голосов
/ 02 марта 2011

мой предыдущий пример с другим вложенным объектом и вашими советами:

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)

дает ожидаемый результат:

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

Спасибо.

22 голосов
/ 02 марта 2011

Итак, непосредственная проблема заключается в том, что вы передаете модулю json значение JSON, которое будет закодировано как просто еще одна строка в значении JSON.

Более общая проблема заключается в том, что вы сильно усложняетеthis.

Рисуя JSON datetime между Python и JavaScript , я бы подошел к чему-то более близкому к этому:

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)

, который дает вам:

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

Это может быть сделано чище / разумнее / безопаснее (в частности, просто захват __dict__ обычно не рекомендуется делать вне отладки / устранения неполадок), но оно должно помочь.По сути, все, что вам нужно, - это способ получить json-совместимый объект (будь то простая строка или число, или список, или тип) из каждого «узла» в дереве.Этот объект должен , а не быть уже сериализованным в JSON объектом, что вы и делали.

6 голосов
/ 31 января 2015

Чтобы избежать повторения кода, как в ответе Фреда Лорана, я перегрузил метод __iter__() следующим образом. Это также позволяет «jsonize» элементам списка, datetime и decimal без дополнительных зависимостей, просто используйте 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()

Выход:

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

Надеюсь, это поможет! Спасибо

2 голосов
/ 21 апреля 2012

Я не могу добавить это как комментарий и добавить как ответ.Последний пример Фреда был полезен для меня. Мне сказали, что jsonpickle делает это, но не смог заставить модуль установить и работать должным образом.Так что использовал код здесь.Небольшая настройка, однако, у меня было слишком много переменных, чтобы добавить их вручную к некоторым объектам.Итак, этот маленький цикл упрощает вещи:

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

Он может использоваться в любом объекте, у которого есть подкласс, который слишком занят, чтобы кодировать вручную.Или можно сделать помощником для всех классов.Это также работает для полного представления в JSON массивов-членов, содержащих другие классы (если, конечно, они реализуют reprJSON ()).

0 голосов
/ 19 сентября 2014

Это то, что вы ищете: https://github.com/jsonpickle/jsonpickle

Он выполняет вложенную сериализацию объектов Python и может быть легко расширен для сериализации пользовательских типов.

...