我必须从一个包含一些无效语法键的JSON对象中建模一个pydantic类.

举个例子:

example = {
    "$type": "Menu",
    "name": "lunch",
    "children": [
        {"$type": "Pasta", "title": "carbonara"},
        {"$type": "Meat", "is_vegetable": false},
    ]
}

我的无聊课程目前看起来是这样的:

class Pasta(BaseModel):
    title: str

class Meat(BaseModel):
    is_vegetable: bool

class Menu(BaseModel):
    name: str
    children: list[Pasta | Meat]

现在,这项工作除了$type场.如果字段名为"dollar_type",我将简单地创建以下TranslationModel基类,并让PastaMeat Menu 继承TranslationModel:

class TranslationModel(BaseModel):

    @computed_field
    def dollar_type(self) -> str:
        return self.__class__.__name__

所以通过执行Menu(**example).model_dump(),我就得到了

{
  'dollar_type': 'Menu', 
  'name': 'lunch', 
  'children': [
    {'dollar_type': 'Pasta', 'title': 'carbonara'}, 
    {'dollar_type': 'Meat', 'is_vegetable': False}
  ]
}

但遗憾的是,我必须严格遵循原始的json struct ,所以我必须使用$type. 我已经按照文档使用了alias model_validator ,但没有成功.

我怎么能解决这个问题呢? 提前谢谢你

推荐答案

这看起来很像是您更愿意应用区别对待的联合模式.请参阅以下示例:

from pydantic import BaseModel, Field
from typing import Literal, Annotated


example = {
    "$type": "Menu",
    "name": "lunch",
    "children": [
        {"$type": "Pasta", "title": "carbonara"},
        {"$type": "Meat", "is_vegetable": False},
    ]
}


class Pasta(BaseModel):
    type: Literal["Pasta"] = Field("Pasta", alias="$type")
    title: str

class Meat(BaseModel):
    type: Literal["Meat"] = Field("Meat", alias="$type")
    is_vegetable: bool


AnyDish = Annotated[Pasta | Meat, Field(discriminator="type")]

class Menu(BaseModel):
    name: str
    children: list[AnyDish]


menu = Menu.model_validate(example)
print(menu)

menu.model_dump(by_alias=True)

它打印:

name='lunch' children=[Pasta(type='Pasta', title='carbonara'), Meat(type='Meat', is_vegetable=False)]
{'name': 'lunch', 'children': [{'$type': 'Pasta', 'title': 'carbonara'}, {'$type': 'Meat', 'is_vegetable': False}]}

这种模式有多个优点:

  • 它将类名从标记中分离出来.这通常是首选的,因为类名可能会更改.但你可能仍然想要阅读旧文件.
  • 该模式是可扩展和显式的.以后你可以很容易地添加新的菜肴类型,只需将它们包括在AnyDish种菜肴中即可.

为了减少冗长,人们还可以引入一条捷径,比如:

class TypeLiteral:
    def __class_getitem__(cls, tag: str):
        return Annotated[Literal[tag], Field(default=tag, alias="$type")]


class Pasta(BaseModel):
    type: TypeLiteral["Pasta"]
    title: str

class Meat(BaseModel):
    type: TypeLiteral["Meat"]
    is_vegetable: bool

你可以在PYDING DOCKS中找到更多关于歧视unions 的信息:https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions

我希望这是有用的!

Python相关问答推荐

Python无法在已导入的目录中看到新模块

使用多个性能指标执行循环特征消除

Python Hashicorp Vault库hvac创建新的秘密版本,但从先前版本中删除了密钥

numba jitClass,记录类型为字符串

如何将双框框列中的成对变成两个新列

将两只Pandas rame乘以指数

从numpy数组和参数创建收件箱

将图像拖到另一个图像

基于字符串匹配条件合并两个帧

使用NeuralProphet绘制置信区间时出错

SQLAlchemy bindparam在mssql上失败(但在mysql上工作)

如何从需要点击/切换的网页中提取表格?

如何杀死一个进程,我的Python可执行文件以sudo启动?

在输入行运行时停止代码

如果包含特定值,则筛选Groupby

如何在Python中使用Iscolc迭代器实现观察者模式?

使用类型提示进行类型转换

仅使用预先计算的排序获取排序元素

浏览超过10k页获取数据,解析:欧洲搜索服务:从欧盟站点收集机会的微小刮刀&

对数据帧进行分组,并按组间等概率抽样n行