在JSON中编码嵌套的python对象

Fre*_*ent 34 python json encode

我想用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()
Run Code Online (Sandbox Code Playgroud)

结果是(转储返回一个字符串表示,这就是"被转义"的原因)

{"abc": "{\"name\": \"abc name\"}"}
Run Code Online (Sandbox Code Playgroud)

我想要一些不同的东西.预期的结果是

{"abc": {"name": "abc name"}"}
Run Code Online (Sandbox Code Playgroud)

但我不知道如何...任何提示?

提前致谢.

Fre*_*ent 42

我之前的示例,另一个嵌套对象和您的建议:

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)
Run Code Online (Sandbox Code Playgroud)

产生预期的结果:

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"}
Run Code Online (Sandbox Code Playgroud)

谢谢.


Nic*_*ght 23

所以,当前的问题是你正在向json模块传递一个JSON值,它将被编码为JSON值中的另一个字符串.

更广泛的问题是你大大过分复杂了.

借鉴Python和JavaScript之间的JSON日期时间,我会更接近于此:

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)
Run Code Online (Sandbox Code Playgroud)

哪个让你:

~$ python nestjson.py 
{"abc": "abc name"}
~$ 
Run Code Online (Sandbox Code Playgroud)

这可以变得更干净/更安全/更安全(特别是,__dict__通常不建议在外部调试/故障排除时使用抓取),但它应该得到重点.从根本上说,您所需要的只是从树中的每个"节点"中获取与json兼容的对象(无论是简单的字符串或数字,还是列表或字典)的方法.该对象应该是已经JSON序列化的对象,这正是您正在做的事情.


Jea*_*raz 6

为了避免像Fred Laurent的回答那样重复代码,我__iter__()按如下方式重载了方法.这也允许'jsonize'列表元素,日期时间和小数,没有额外的依赖,只需使用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()
Run Code Online (Sandbox Code Playgroud)

输出:

-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"
    }
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你!谢谢


ced*_*mos 6

尽管我认为所有其他解决方案都有效,但我发现它们确实有很多样板代码,而目标是仅对嵌套的 python 对象进行编码。

在一篇文章中,我找到了一个优雅的解决方案,它完全符合您的要求,但没有样板代码。因为您甚至可以免费获得反序列化部分,所以我将首先向您展示您的确切问题的解决方案,然后提供一个更清晰的版本,其中反序列化也可以工作。

您的问题的准确解决方案

import json


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


class Doc(object):
    def __init__(self):
        self.abc = Abc()


doc = Doc()

# Serialization
json_data = json.dumps(doc, default=lambda o: o.__dict__)
print(json_data)
Run Code Online (Sandbox Code Playgroud)

这将准确输出您所要求的内容:

{"abc": {"name": "abc name"}}
Run Code Online (Sandbox Code Playgroud)

更优雅的解决方案来启用序列化和反序列化

import json


class Abc(object):
    def __init__(self, name: str):
        self.name = name


class Doc(object):
    def __init__(self, abc):
        self.abc = abc


abc = Abc("abc name")
doc = Doc(abc)

# Serialization
json_data = json.dumps(doc, default=lambda o: o.__dict__)
print(json_data)

# De-serialization
decoded_doc = Doc(**json.loads(json_data))
print(decoded_doc)
print(vars(decoded_doc))
Run Code Online (Sandbox Code Playgroud)

这将输出以下内容:

{"abc": {"name": "abc name"}}
<__main__.Doc object at 0x7ff75366f250>
{'abc': {'name': 'abc name'}}
Run Code Online (Sandbox Code Playgroud)

整个魔力是通过定义一个默认的 lambda 函数来实现的json_data = json.dumps(doc, default=lambda o: o.__dict__)