从 json 初始化一个 pydantic 数据类

Initializing a pydantic dataclass from json

我正在将项目中现有的 dataclasses 转换为 pydantic-dataclasses,我正在使用这些 dataclasses 来表示我需要编码到和从 json.

解析

这是我目前的方法的一个例子,它对我的​​用例来说不够好,我有一个 class A 我想把它都转换成一个字典(稍后转换成书面的作为 json) 并从该字典中读取。 但是我能找到将 json 解析回模型的唯一方法让我返回底层 BaseModel 而不是 dataclass.

请注意,我正在使用 asdict 函数将 dataclass 转换为 dict,因为它是 pydantic_encoder 用来转换 dataclass 到 json,并使用文档推荐的 pydantic_encoderpydantic-dataclass 转换为 json: https://pydantic-docs.helpmanual.io/usage/dataclasses/

from dataclasses import asdict
from pydantic.dataclasses import dataclass
from pydantic import BaseModel

@dataclass
class A:
    x: str

a = A("string")
a_dict = asdict(a)
parsed_a = A.__pydantic_model__.parse_obj(a_dict)

print(f"type of a: {type(a)}")
print(f"type of parsed_a: {type(parsed_a)}")

print(f"a is instance of A: {isinstance(a, A)}")
print(f"parsed_a is instance of A: {isinstance(parsed_a, A)}")

print(f"a is instance of BaseModel: {isinstance(a, BaseModel)}")
print(f"parsed_a is instance of BaseModel: {isinstance(parsed_a, BaseModel)}")

输出:

type of a: <class '__main__.A'>
type of parsed_a: <class '__main__.A'>
a is instance of A: True
parsed_a is instance of A: False
a is instance of BaseModel: False
parsed_a is instance of BaseModel: True

有没有办法从解析的 BaseModel 初始化 A

我设法通过解压缩已解析 BaseModel 的属性并用它们初始化 dataclass 来解决这个问题。

此解决方案递归地适用于属性为 pydantic-dataclasses 或 primitives 的 Pydantic 数据类

请注意,此解决方案不适用于联合和泛型(目前)。

 def pydantic_dataclass_from_json_dict(json_dict: dict, pydantic_dataclass_type) -> Any:
    base_model = pydantic_dataclass_type.__pydantic_model__.parse_obj(json_dict)
    base_mode_fields = base_model.__fields__
    dataclass_fields = dataclasses.fields(pydantic_dataclass_type)

    values = []
    for base_model_field_name, base_model_field in base_mode_fields.items():
        value = getattr(base_model, base_model_field_name)
        dataclass_field = [field for field in dataclass_fields if field.name == base_model_field.name][0]
        if is_dataclass(dataclass_field):
            converted_value = pydantic_dataclass_from_json_dict(value, dataclass_field.type)
            values.append(converted_value)
        else:
            values.append(value)

    dataclass_object = pydantic_dataclass_type(*values)
    return dataclass_object

我想我来晚了一点,但我认为这个答案对于未来有同样问题的用户可能会派上用场。

要将 dataclass 转换为 json,您可以使用您已经在使用的组合(asdictjson.dump)。

from pydantic.dataclasses import dataclass

@dataclass
class User:
  id: int
  name: str

user = User(id=123, name="James")
d = asdict(user)  # {'id': 123, 'name': 'James'
user_json = json.dumps(d)
print(user_json)  # '{"id": 123, "name": "James"}'

# Or directly with pydantic_encoder
json.dumps(user, default=pydantic_encoder)

然后从原始 json 你可以使用 BaseModelparse_raw 方法。

如果你想将json反序列化为pydantic个实例,我推荐你使用parse_raw方法:

user = User.__pydantic_model__.parse_raw('{"id": 123, "name": "James"}')
print(user)
# id=123 name='James'

否则,如果您想保留数据类:

json_raw = '{"id": 123, "name": "James"}'
user_dict = json.loads(json_raw)
user = User(**user_dict)