在 JSON 中编码嵌套的 python 对象

Posted

技术标签:

【中文标题】在 JSON 中编码嵌套的 python 对象【英文标题】:Encoding nested python object in JSON 【发布时间】:2011-07-06 19:33:21 【问题描述】:

我想用 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()

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

"abc": "\"name\": \"abc name\""

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

"abc": "name": "abc name""

但我不知道如何... 有什么提示吗?

提前致谢。

【问题讨论】:

查看***.com/a/63718624/1497139 以获得更一般性问题的答案 【参考方案1】:

因此,最直接的问题是您正在向 json 模块传递一个 JSON 值,该值将被编码为 JSON 值中的另一个字符串。

更广泛的问题是您将其复杂化了很多。

借鉴JSON datetime between Python and 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 的对象,这就是你正在做的。

【讨论】:

【参考方案2】:

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

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"

谢谢。

【讨论】:

已经快 9 年了,您的回答仍然适用于 Python 3.8,很好 :) 谢谢!【参考方案3】:

我无法将此添加为评论并添加为答案。 Fred 的最终示例对我很有用。有人告诉我 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())。

【讨论】:

这太棒了,让我可以让它成为类的一部分,并以不同的方式处理日期时间对象等属性。另外,继承类可以调用超定义并将自己的处理应用于某些属性。 查看***.com/a/63718624/1497139 了解扩展版本【参考方案4】:

这就是您要查找的内容:https://github.com/jsonpickle/jsonpickle

它对 Python 对象进行嵌套序列化,并且可以轻松扩展以序列化自定义类型。

【讨论】:

实际上并没有【参考方案5】:

为了避免重复 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()

输出:

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

希望对您有所帮助! 谢谢

【讨论】:

这是一个绝妙的解决方案。我的问题是:你怎么能做同样的事情,但限制可以被 json 化的内容(例如,我如何从 Identity 对象中省略 name 属性?)【参考方案6】:

虽然我认为所有其他解决方案都可以工作,但我发现 它们确实有很多样板代码,但目标是只对嵌套的 Python 对象进行编码。

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

问题的准确解决方案

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)

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

"abc": "name": "abc name"

实现序列化和反序列化的更优雅的解决方案

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

这将输出以下内容:

"abc": "name": "abc name"
<__main__.Doc object at 0x7ff75366f250>
'abc': 'name': 'abc name'

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

【讨论】:

以上是关于在 JSON 中编码嵌套的 python 对象的主要内容,如果未能解决你的问题,请参考以下文章

Django:如何遍历 html 中的嵌套 Json 对象

Swift/JSONEncoder:包含嵌套原始 JSON 对象文字的编码类

使用python打印嵌套在数组中的json对象

python json快速解析命令

在 python 中删除某些嵌套的 JSON 对象时遇到问题

python: json模块 --JSON编码和解码