从 json 初始化 pydantic 数据类

hag*_*aiw 34 python python-dataclasses pydantic

我正在将dataclasses项目中现有的转换为pydantic-dataclasses,我使用这些dataclasses来表示我需要编码和解析 json 的模型。

这是我当前方法的一个示例,该方法对于我的用例来说不够好,我有一个类A,我想将其转换为字典(稍后转换为 json)并从该字典中读取。但我能找到的将 json 解析回模型的唯一方法给了我底层BaseModel而不是dataclass.

请注意,我使用该asdict函数将 a 转换dataclass为 a dict,因为它是用来pydantic_encoder将 a 转换dataclass为 json 的,并使用pydantic_encoder文档建议的将 a 转换pydantic-dataclass为 json: https: //pydantic-docs.helpmanual.io/usage /数据类/

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

输出:

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

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

Gui*_*lem 26

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

要将其转换dataclass为 json,您可以使用已经使用的组合(asdict加号json.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)
Run Code Online (Sandbox Code Playgroud)

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

如果你想将 json 反序列化为pydantic实例,我建议你使用以下parse_raw方法:

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

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

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

  • 解析是否递归进行? (2认同)

pce*_*con 11

我认为现在可以通过简单的方式解决这个问题。添加到@Guillem示例:

from pydantic.dataclasses import dataclass
from pydantic.tools import parse_obj_as
import dataclasses
import json

@dataclass
class User:
  id: int
  name: str

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

user_dict = json.loads(user_json)
user = parse_obj_as(User, user_dict)
print(user)  # User(id=123, name='James')
Run Code Online (Sandbox Code Playgroud)

它也适用于递归。

  • 从 Pydantic v2 开始,`parse_obj_as` 已被弃用 https://docs.pydantic.dev/latest/migration/ (4认同)

小智 8

Pydantic建议使用parse_raw反序列化 JSON 字符串。

类定义

from pydantic import BaseModel

class ResponseData(BaseModel):
    status_code: int
    text: str
    reason: str
    
    class Config:
        orm_mode = True
Run Code Online (Sandbox Code Playgroud)

JSON 到 BaseModel 的转换

x = ResponseData(status_code=200, text="", reason="")
json = x.json()
response = ResponseData.parse_raw(json)
assert x == response
print(response.dict())
Run Code Online (Sandbox Code Playgroud)

  • 据我所知,这是截至 2023 年 2 月当前推荐的方法:https://docs.pydantic.dev/usage/exporting_models/#custom-json-deserialization (2认同)

AMT*_*erp 2

为了建立在其他人的答案的基础上,因为正确/最佳的答案似乎不断变化:

使用 PydanticTypeAdapter是目前的最佳选择。更新了@pceccon 示例:

from pydantic.dataclasses import dataclass
from pydantic import TypeAdapter
import dataclasses
import json

@dataclass
class User:
  id: int
  name: str

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

user = TypeAdapter(User).validate_json(user_json)
print(user)  # User(id=123, name='James')
print(user.name)  # 'James'
Run Code Online (Sandbox Code Playgroud)

如果您希望 PyCharm(或任何编辑器)为您提供自动完成/智能感知,请使用类型提示:

user: User = TypeAdapter(User).validate_json(user_json)
Run Code Online (Sandbox Code Playgroud)

如果你有字典而不是 json,你可以使用TypeAdapter.validate_python

user: User = TypeAdapter(User).validate_python(user_dict)

Run Code Online (Sandbox Code Playgroud)

这对于您的模型/类定义递归地起作用。