99

从 Python 3.7 开始,有一种叫做数据类的东西:

from dataclasses import dataclass

@dataclass
class Foo:
    x: str

但是,以下失败:

>>> import json
>>> foo = Foo(x="bar")
>>> json.dumps(foo)
TypeError: Object of type Foo is not JSON serializable

如何将json.dumps()实例编码Foo为 json对象

4

10 回答 10

132

就像您可以为datetime对象或小数添加对 JSON 编码器的支持一样,您也可以提供自定义编码器子类来序列化数据类:

import dataclasses, json

class EnhancedJSONEncoder(json.JSONEncoder):
        def default(self, o):
            if dataclasses.is_dataclass(o):
                return dataclasses.asdict(o)
            return super().default(o)

json.dumps(foo, cls=EnhancedJSONEncoder)
于 2018-07-11T13:29:38.393 回答
58

您不能只使用该dataclasses.asdict()函数将数据类转换为字典吗?就像是:

>>> @dataclass
... class Foo:
...     a: int
...     b: int
...     
>>> x = Foo(1,2)
>>> json.dumps(dataclasses.asdict(x))
'{"a": 1, "b": 2}'
于 2019-01-10T00:59:55.163 回答
39

获取 JSONified 数据类实例的方法

有几个选项可以实现该目标,每个选项的选择都意味着分析哪种方法最适合您的需求:

标准库:dataclass.asdict

import dataclasses
import json


@dataclass.dataclass
class Foo:
    x: str

foo = Foo(x='1')
json_foo = json.dumps(dataclasses.asdict(foo)) # '{"x": "1"}'

将其取回数据类实例并非易事,因此您可能想访问该答案https://stackoverflow.com/a/53498623/2067976

棉花糖数据类

from dataclasses import field
from marshmallow_dataclass import dataclass


@dataclass
class Foo:
    x: int = field(metadata={"required": True})

foo = Foo(x='1') # Foo(x='1')
json_foo = foo.Schema().dumps(foo) # '{"x": "1"}'

# Back to class instance.
Foo.Schema().loads(json_foo) # Foo(x=1)

作为奖励,marshmallow_dataclass您可以在字段本身上使用验证,当有人使用该模式从 json 反序列化对象时,将使用该验证。

数据类 Json

from dataclasses import dataclass
from dataclasses_json import dataclass_json


@dataclass_json
@dataclass
class Foo:
    x: int

foo = Foo(x='1')
json_foo = foo.to_json() # Foo(x='1')
# Back to class instance
Foo.from_json(json_foo) # Foo(x='1')

此外,除了该通知之外,marshmallow 数据类为您进行了类型转换,而 dataclassses-json(ver.: 0.5.1) 忽略了这一点。

编写自定义编码器

遵循接受的 Miracle2k 答案并重用自定义 json 编码器。

于 2020-07-03T13:41:40.740 回答
11

如果您可以为此使用库,则可以使用dataclasses-json。这是一个例子:

from dataclasses import dataclass

from dataclasses_json import dataclass_json


@dataclass_json
@dataclass
class Foo:
    x: str


foo = Foo(x="some-string")
foo_json = foo.to_json()

它还支持嵌入式数据类- 如果您的数据类有一个类型为另一个数据类的字段 - 如果所有涉及的数据类都具有@dataclass_json装饰器。

于 2020-01-10T19:47:00.850 回答
6

我建议使用以下to_json()方法为您的数据类创建一个父类:

import json
from dataclasses import dataclass, asdict

@dataclass
class Dataclass:
    def to_json(self) -> str:
        return json.dumps(asdict(self))

@dataclass
class YourDataclass(Dataclass):
    a: int
    b: int

x = YourDataclass(a=1, b=2)
x.to_json()  # '{"a": 1, "b": 2}'

如果您有其他功能要添加到所有数据类中,这将特别有用。

于 2020-12-30T09:15:57.207 回答
3

您还可以在类中实现asdictandjson.dumps方法。在这种情况下,不需要导入json.dumps项目的其他部分:


from typing import List
from dataclasses import dataclass, asdict, field
from json import dumps


@dataclass
class TestDataClass:
    """
    Data Class for TestDataClass
    """
    id: int
    name: str
    tested: bool = False
    test_list: List[str] = field(default_factory=list)

    @property
    def __dict__(self):
        """
        get a python dictionary
        """
        return asdict(self)

    @property
    def json(self):
        """
        get the json formated string
        """
        return dumps(self.__dict__)


test_object_1 = TestDataClass(id=1, name="Hi")
print(test_object_1.__dict__)
print(test_object_1.json)

输出:

{'id': 1, 'name': 'Hi', 'tested': False, 'test_list': []}
{"id": 1, "name": "Hi", "tested": false, "test_list": []}

您还可以创建一个父类来继承这些方法:

from typing import List
from dataclasses import dataclass, asdict, field
from json import dumps


@dataclass
class SuperTestDataClass:

    @property
    def __dict__(self):
        """
        get a python dictionary
        """
        return asdict(self)

    @property
    def json(self):
        """
        get the json formated string
        """
        return dumps(self.__dict__)


@dataclass
class TestDataClass(SuperTestDataClass):
    """
    Data Class for TestDataClass
    """
    id: int
    name: str
    tested: bool = False
    test_list: List[str] = field(default_factory=list)


test_object_1 = TestDataClass(id=1, name="Hi")
print(test_object_1.__dict__)
print(test_object_1.json)


于 2021-11-30T08:30:21.787 回答
3

编码dataclass和对象的最简单方法是为无法以其他方式序列化的对象SimpleNamespace提供默认函数,并返回对象:json.dumps()__dict__

json.dumps(foo, default=lambda o: o.__dict__)
于 2021-11-27T11:21:10.853 回答
2

使用字典解包可以在 Reddit上找到更简单的答案

>>> from dataclasses import dataclass
>>> @dataclass
... class MyData:
...   prop1: int
...   prop2: str
...   prop3: int
...
>>> d = {'prop1': 5, 'prop2': 'hi', 'prop3': 100}
>>> my_data = MyData(**d)
>>> my_data
MyData(prop1=5, prop2='hi', prop3=100)
于 2020-09-05T14:41:46.530 回答
0

好的,这就是我在类似情况下所做的。

  1. 创建一个自定义字典工厂,将嵌套数据类转换为字典。

    def myfactory(data): return dict(x for x in data if x[1] is not None)

  2. 如果 foo 是您的 @dataclass,那么只需提供您的字典工厂以使用“myfactory()”方法:

    fooDict = asdict(foo, dict_factory=myfactory)

  3. 将 fooDict 转换为 json

    fooJson = json.dumps(fooDict)

这应该工作!

于 2021-07-29T16:06:32.017 回答
0

dataclass-wizard是一个适合您的现代选项。它支持复杂的类型,例如日期和时间、typing模块中的大多数泛型以及嵌套的数据类结构。

PEP 585604中引入的“新样式”注释可以通过__future__如下所示的导入移植回 Python 3.7 。

from __future__ import annotations  # This can be removed in Python 3.10
from dataclasses import dataclass, field
from dataclass_wizard import JSONWizard


@dataclass
class MyClass(JSONWizard):
    my_str: str | None
    is_active_tuple: tuple[bool, ...]
    list_of_int: list[int] = field(default_factory=list)


string = """
{
  "my_str": 20,
  "ListOfInt": ["1", "2", 3],
  "isActiveTuple": ["true", false, 1]
}
"""

instance = MyClass.from_json(string)
print(repr(instance))
# MyClass(my_str='20', is_active_tuple=(True, False, True), list_of_int=[1, 2, 3])

print(instance.to_json())
# '{"myStr": "20", "isActiveTuple": [true, false, true], "listOfInt": [1, 2, 3]}'

# True
assert instance == MyClass.from_json(instance.to_json())

您可以使用以下命令安装 Dataclass Wizard pip

$ pip install dataclass-wizard

一点背景资料:

对于序列化,它使用稍微修改(更有效)的dataclasses.asdict. 在将 JSON 反序列化为数据类实例时,它第一次迭代数据类字段并为每个带注释的类型生成一个解析器,这使得反序列化过程多次运行时效率更高。

免责声明:我是这个库的创建者(和维护者)。

于 2021-08-30T15:29:55.420 回答