使Python json编码器支持Python的新数据类

mir*_*e2k 23 python python-dataclasses

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

from dataclasses import dataclass

@dataclass
class Foo:
    x: str
Run Code Online (Sandbox Code Playgroud)

但是,以下失败:

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

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

mir*_*e2k 35

就像您可以为datetime对象或Decimal 添加对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)
Run Code Online (Sandbox Code Playgroud)

  • 嵌套怎么样 - 例如序列化 `d={ 'a': Foo(1,2), 'b': Foo(3,4)}` (3认同)
  • 使用像这样的 json 编码器可以很好地处理嵌套 - json 序列化器将使用它来递归地转换子元素。 (2认同)
  • 另外,反过来呢?如果我有 json 文件形式的数据并希望将其加载回我的数据类对象怎么办? (2认同)

And*_*yko 21

获取 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"}'
Run Code Online (Sandbox Code Playgroud)

将它选回数据类实例并非易事,因此您可能想访问该答案/sf/answers/3744903641/

棉花糖数据类

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)
Run Code Online (Sandbox Code Playgroud)

作为奖励,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')
Run Code Online (Sandbox Code Playgroud)

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

编写自定义编码器

遵循接受的奇迹 2k 答案并重用自定义 json 编码器。


Ren*_*ene 15

您还可以在类中实现asdictand方法。json.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)

Run Code Online (Sandbox Code Playgroud)

输出:

{'id': 1, 'name': 'Hi', 'tested': False, 'test_list': []}
{"id": 1, "name": "Hi", "tested": false, "test_list": []}
Run Code Online (Sandbox Code Playgroud)

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

{'id': 1, 'name': 'Hi', 'tested': False, 'test_list': []}
{"id": 1, "name": "Hi", "tested": false, "test_list": []}
Run Code Online (Sandbox Code Playgroud)


mrt*_*rts 12

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

json.dumps(foo, default=lambda o: o.__dict__)
Run Code Online (Sandbox Code Playgroud)


Jun*_*ius 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()
Run Code Online (Sandbox Code Playgroud)

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


Phi*_*pak 11

我建议使用以下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}'
Run Code Online (Sandbox Code Playgroud)

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


Chu*_*ers 10

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

>>> @dataclass
... class Foo:
...     a: int
...     b: int
...     
>>> x = Foo(1,2)
>>> json.dumps(dataclasses.asdict(x))
'{"a": 1, "b": 2}'
Run Code Online (Sandbox Code Playgroud)

  • 反过来呢?如果我有 json 文件形式的数据并希望将其加载回我的数据类对象怎么办? (4认同)
  • 数据类可能是大型结构的深层嵌套部分。通过使用自定义编码器,您可以执行json.dumps({“ obj”:[something_that_may_or_may_not_contain_a_dataclass]}) (2认同)
  • asdict() 将正确处理所有嵌套数据类,因此,我们得到通常加载到 jison 字符串中的嵌套字典(但是!例如,在加载到字符串之前,必须另外处理日期时间类型) (2认同)