将类实例序列化为JSON

259

我试图创建一个类实例的JSON字符串表示,但是遇到了困难。假设这个类是像这样构建的:

class testclass:
    value1 = "a"
    value2 = "b"

一个调用json.dumps的例子如下:
t = testclass()
json.dumps(t)

它失败了,并告诉我测试类不可JSON序列化。

TypeError: <__main__.testclass object at 0x000000000227A400> is not JSON serializable

我也尝试使用pickle模块:

t = testclass()
print(pickle.dumps(t, pickle.HIGHEST_PROTOCOL))

它提供类实例信息,但不提供类实例的序列化内容。
b'\x80\x03c__main__\ntestclass\nq\x00)\x81q\x01}q\x02b.'

我做错了什么?


https://dev59.com/rnE95IYBdhLWcg3wXcrd - CodeClown42
50
使用一行代码s = json.dumps(obj, default=lambda x: x.__dict__)将对象的实例变量(self.value1self.value2,...)进行序列化。这是最简单和最直接的方法,它可以序列化嵌套的对象结构。当任何给定对象不是直接可序列化时,将调用default函数。你也可以看看我下面的答案。我发现流行的答案过于复杂,可能在很长时间以前是正确的。 - codeman48
2
你的 testclass 没有 __init__() 方法,因此所有实例将共享在类语句中定义的两个类属性 (value1value2)。你明白类和实例之间的区别吗? - martineau
2
有一个Python库可以实现这个功能,它的网址是https://github.com/jsonpickle/jsonpickle。 - best wishes
17个回答

3

使用任意的、可扩展的对象,然后将其序列化为JSON:

import json

class Object(object):
    pass

response = Object()
response.debug = []
response.result = Object()

# Any manipulations with the object:
response.debug.append("Debug string here")
response.result.body = "404 Not Found"
response.result.code = 404

# Proper JSON output, with nice formatting:
print(json.dumps(response, indent=4, default=lambda x: x.__dict__))

2
有一些好的答案可以帮助你开始做这件事。但是需要注意以下几点:
  • 如果实例嵌套在大型数据结构中怎么办?
  • 如果还想要类名怎么办?
  • 如果想反序列化实例怎么办?
  • 如果使用__slots__而不是__dict__怎么办?
  • 如果你只是不想自己做怎么办?

json-tricks是一个库(我和其他人共同贡献)已经能够做到这一点了。例如:

class MyTestCls:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

cls_instance = MyTestCls(s='ub', dct={'7': 7})

json = dumps(cls_instance, indent=4)
instance = loads(json)

你会收到你的实例。这里的json看起来像这样:
{
    "__instance_type__": [
        "json_tricks.test_class",
        "MyTestCls"
    ],
    "attributes": {
        "s": "ub",
        "dct": {
            "7": 7
        }
    }
}

如果您想制作自己的解决方案,可以查看json-tricks的源代码,以免忘记一些特殊情况(例如__slots__)。
它还支持其他类型,如numpy数组、日期时间、复数;还可以允许添加注释。

2

我在Flask应用程序中一直使用的方法是将类实例序列化为JSON响应。

参考Github项目

from json import JSONEncoder
import json
from typing import List

class ResponseEncoder(JSONEncoder):
    def default(self, o):
        return o.__dict__

class ListResponse:
    def __init__(self, data: List):
        self.data = data
        self.count = len(data)

class A:
    def __init__(self, message: str):
        self.message = message

class B:
    def __init__(self, record: A):
        self.record = record

class C:
    def __init__(self, data: B):
        self.data = data

现在创建 A、B、C 的实例,然后进行编码。

data_a = A('Test Data')
data_b = B(data_a)
data_c = C(data_b)

response = ResponseEncoder().encode(data_c)
json_response = json.loads(response)

输出

{
    "data": {
        "record": {
            "message": "Test Data"
        }
    }
}

对于列表类型的响应

records = ['One', 'Two', 'Three']
list_response = ListResponse(records)
response = ResponseEncoder().encode(list_response)
json_response = json.loads(response)

输出

{
    "data": [
        "One",
        "Two",
        "Three"
    ],
    "count": 3
}

2
您可以使用Jsonic将几乎任何内容序列化为JSON:
示例:

https://github.com/OrrBin/Jsonic

class TestClass:
def __init__(self):
    self.x = 1
    self.y = 2

instance = TestClass()
s = serialize(instance): # instance s set to: {"x":1, "y":2}
d = deserialize(s) # d is a new class instance of TestClass
< p > < em > Jsonic < /em > 具有声明类属性为瞬态和类型安全反序列化等一些不错的特性。 < /p > < p > (虽然答案有点晚,但我认为它可能对其他人有帮助)< /p >

1
我为此编写了一个函数,它运行得相当不错:

def serialize(x,*args,**kwargs):
    kwargs.setdefault('default',lambda x:getattr(x,'__dict__',dict((k,getattr(x,k) if not callable(getattr(x,k)) else repr(getattr(x,k))) for k in dir(x) if not (k.startswith('__') or isinstance(getattr(x,k),x.__class__)))))
    return json.dumps(x,*args,**kwargs)

1

这里有另一种非常简单而优雅的方法,可以应用于此处,那就是只需子类化“dict”,因为它默认是可序列化的。

from json import dumps

class Response(dict):
    def __init__(self, status_code, body):
        super().__init__(
            status_code = status_code,
            body = body
        )

r = Response()
dumps(r)

这会导致一些奇怪的结果,可能是可以接受的,但令人困惑: r = Response(200, 'blah'); r['a'] = 1; r.b = 2 的结果是 r == {'status_code': 200, 'body': 'blah', 'a': 1}r.__dict__ == {'b': 2}。我很想知道 dict 实际上存储它的键和值的位置。 - naught101

0
你可以尝试使用objprint,这是一个轻量级的库,用于打印Python对象,并支持json输出。
pip install objprint

from objprint import objjson
t = testclass()
json_obj = objjson(t)
print(json.dumps(json_obj))

objjson 基本上将任意对象转换为可转换为 JSON 的对象,其中包含一个特殊的键 .type,用于表示它的原始 Python 类型,如果它不是内置类型,如 dict、list 等。

如果您只想打印它,可以使用 op,通常用于以人类可读格式打印对象。

from objprint import op
t = testclass()
op(t, format="json", indent=2)

# If you want to dump to a file
with open("my_obj.json", "w") as f:
    # This is the same usage as print
    op(t, format="json", file=f)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接