在 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 对象的主要内容,如果未能解决你的问题,请参考以下文章
Swift/JSONEncoder:包含嵌套原始 JSON 对象文字的编码类