在 JSON 中编码嵌套 python 对象

发布于 2024-10-19 16:38:16 字数 855 浏览 4 评论 0原文

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

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

提前致谢。

I want to encode objects in JSON. But, I can not figure out how to make the output without the string escaping.

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

The result is (the dumps returns a string representation, that's why the " are escaped)

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

I want something a little bit different. The expected result is

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

But I don't see how to...
Any hint ?

thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

比忠 2024-10-26 16:38:17

为了避免重复 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"
    }
}

希望有帮助!
谢谢

To avoid repetition of code like in Fred Laurent's answer I overloaded the __iter__() method as follows. This also permits to 'jsonize' list elements, datetime and decimal with no extra dependencies, just use 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()

The output:

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

Hope it helps!
Thanks

灼疼热情 2024-10-26 16:38:17

尽管我认为所有其他解决方案都有效,但我发现它们确实有很多样板代码,而目标是仅对嵌套的 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__)

Although all the other solutions I assume they work I find they do have a lot of boilerplate code, when the goal is to only encode nested python objects.

In an article I found an elegant solution, which does exactly what you asked for but without the boilerplate code. As you can even have the de-serialization part for free as well I will show you first a solution to your exact question and then give a cleaner version where the de-serialization will work as well.

Exact solution to your question

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)

This will output exactly what you where asking for:

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

More elegant solution to enable serializing and de-seralizing

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

This will output the following:

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

The whole magic works by defining a default lambda function: json_data = json.dumps(doc, default=lambda o: o.__dict__).

我纯我任性 2024-10-26 16:38:17

我无法将其添加为评论并添加为答案。
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() )。

I could not add this as a comment and adding as answer.
Fred's final sample was useful for me.I was told jsonpickle does this, but could not get the module to install and run properly. So used the code here. Minor tweak though, I had way too many variables to add by hand to some of the objects. So this little loop simplified things:

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

It can be used in any object that has a subclass that is too busy to hand encode. Or can be made a helper for all classes. This also works for the full JSON presentation of member arrays that contain other classes (as long as they implement reprJSON() of course).

客…行舟 2024-10-26 16:38:17

对于更复杂的序列化,我会使用 jsons,它于 2022 年发布。

  • 将 Python 对象转换为字典或 (JSON) 字符串并返回

  • 无需对您的对象进行任何更改

  • 轻松定制和扩展

  • 使用数据类、属性和 POPO

     pip install jsons
      类人:
            名称:str
            生日:日期时间
    
      personObject = Person("托尼", 出生日期)
    
      导入 json
      json_data = jsons.dumps(personObject, 缩进=4)
    

For more complex serialization I would use jsons, it was published in 2022.

  • Turn Python objects into dicts or (JSON)strings and back

  • No changes are required to your objects

  • Easily customizable and extendable

  • Works with data classes, attrs, and POPOs

      pip install jsons
      class Person:
            name:str
            birthday:datetime
    
      personObject = Person("Tony", date_of_birth)
    
      import jsons
      json_data = jsons.dumps(personObject, indent=4)
    
女皇必胜 2024-10-26 16:38:17

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

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

This is what you're looking for: https://github.com/jsonpickle/jsonpickle

It does nested serialization of Python objects and can easily be extended to serialize custom types.

倦话 2024-10-26 16:38:16

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

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

谢谢。

my previous sample, with another nested object and your advices :

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)

produces the expected result :

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

Thanks.

筑梦 2024-10-26 16:38:16

因此,眼前的问题是您向 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 序列化的对象,这正是您正在做的事情。

So, the immediate problem is that you're passing the json module a JSON value, which will get encoded as just another string in the JSON value.

The broader problem is that you're greatly overcomplicating this.

Drawing on JSON datetime between Python and JavaScript, I'd go with something closer to this:

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)

which gets you:

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

This can be made cleaner/saner/safer (in particular, just grabbing __dict__ isn't generally a recommended thing to do outside debugging/troubleshooting), but it should get the point across. All you need, fundamentally, is a way to get a json-compatible object (whether that's a simple string or number, or a list or dict) out of each "node" in the tree. That object should not be an already-JSON-serialized object, which is what you were doing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文