【发布时间】:2021-11-13 17:23:20
【问题描述】:
我有一个带有嵌套数据结构的简单 pydantic 模型。
我希望能够简单地将这个模型的实例保存和加载为 .json 文件。
所有模型都继承自 Base 类,配置简单。
class Base(pydantic.BaseModel):
class Config:
extra = 'forbid' # forbid use of extra kwargs
有一些带有继承的简单数据模型
class Thing(Base):
thing_id: int
class SubThing(Thing):
name: str
还有一个Container 类,其中包含一个Thing
class Container(Base):
thing: Thing
我可以创建一个Container 实例并将其保存为.json
# make instance of container
c = Container(
thing = SubThing(
thing_id=1,
name='my_thing')
)
json_string = c.json(indent=2)
print(json_string)
"""
{
"thing": {
"thing_id": 1,
"name": "my_thing"
}
}
"""
但 json 字符串未指定 thing 字段是使用 SubThing 构造的。因此,当我尝试将此字符串加载到新的 Container 实例中时,我收到错误消息。
print(c)
"""
Traceback (most recent call last):
File "...", line 36, in <module>
c = Container.parse_raw(json_string)
File "pydantic/main.py", line 601, in pydantic.main.BaseModel.parse_raw
File "pydantic/main.py", line 578, in pydantic.main.BaseModel.parse_obj
File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for Container
thing -> name
extra fields not permitted (type=value_error.extra)
"""
有没有一种简单的方法来保存Container 实例,同时保留有关thing 类类型的信息,以便我可以可靠地重建初始Container 实例?如果可能的话,我想避免腌制物体。
一种可能的解决方案是手动序列化,例如使用
def serialize(attr_name, attr_value, dictionary=None):
if dictionary is None:
dictionary = {}
if not isinstance(attr_value, pydantic.BaseModel):
dictionary[attr_name] = attr_value
else:
sub_dictionary = {}
for (sub_name, sub_value) in attr_value:
serialize(sub_name, sub_value, dictionary=sub_dictionary)
dictionary[attr_name] = {type(attr_value).__name__: sub_dictionary}
return dictionary
c1 = Container(
container_name='my_container',
thing=SubThing(
thing_id=1,
name='my_thing')
)
from pprint import pprint as print
print(serialize('Container', c1))
{'Container': {'Container': {'container_name': 'my_container',
'thing': {'SubThing': {'name': 'my_thing',
'thing_id': 1}}}}}
但这消除了利用包进行序列化的大部分好处。
【问题讨论】:
-
你为什么要使用
pydantic——就像你从它提供的验证中受益一样?只是好奇 -
是的,我主要将它用于验证,但原则上我可以使用其他东西。这是我实际应用程序的一个极其简化的版本。
-
只在网上粗略看了一下,看起来这是一个已知问题,
pydantic不支持将嵌套的 json 加载到模型类中,但有计划在未来提供支持用例。实际上,我很惊讶 pydantic 没有将 dict 解析为嵌套模型 - 对我来说似乎是一个足够常见的用例。 -
使用here 提到的根验证器也可能有效
-
我已经使用数据类测试了序列化,并且在大多数情况下都可以完美运行。我确实注意到某些字段类型存在问题,例如
defaultdict字段。看起来数据类没有按预期处理此类字段类型的序列化(我猜它会将其视为普通字典)。您可以使用dataclasses.asdict()帮助函数来序列化数据类实例,这也适用于嵌套数据类。唯一的问题是从字典中反序列化它,不幸的是,这似乎是数据类中缺少的链接。