在 JSON 中编码嵌套的 python 对象

问题描述 投票:0回答:7

我想用 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"}"}

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

提前致谢。

python json encode
7个回答
66
投票

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

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

谢谢。


30
投票

因此,眼前的问题是您向 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)

这让你:

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

这可以变得更干净/更理智/更安全(特别是,在调试/故障排除之外,通常不建议只抓住

__dict__
),但它应该能够传达要点。从根本上讲,您需要的是一种从树中的每个“节点”中获取 json 兼容对象(无论是简单的字符串或数字,还是列表或字典)的方法。该对象不应该是一个已经 JSON 序列化的对象,而这正是您正在做的。


13
投票
__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" } }

希望有帮助!
谢谢


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)

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

{"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__)

    


2
投票

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


2
投票
jsons

,它于 2022 年发布。

    将 Python 对象转换为字典或 (JSON) 字符串并返回
  • 无需对您的对象进行任何更改
  • 轻松定制和扩展
  • 适用于数据类、属性和 POPO
  • pip install jsons class Person: name:str birthday:datetime personObject = Person("Tony", date_of_birth) import jsons json_data = jsons.dumps(personObject, indent=4)

    
        

1
投票
https://github.com/jsonpickle/jsonpickle

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

© www.soinside.com 2019 - 2024. All rights reserved.