You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Pydantic支持含两种可选字典类型的列表验证?

解决方案

原因分析

你当前的SourceInfoItem模型定义了两个可选字段,但Pydantic依然报错,是因为模型默认期望每个列表项的结构同时包含这两个字段(即使值为None),但你的JSON数据中每个对象仅包含其中一个键。这种场景属于互斥的多态类型,需要用联合类型而非包含可选字段的模型来处理。

正确实现方式(Pydantic v2)

使用Pydantic的DiscriminatedUnion定义互斥类型,指定每个类型对应的鉴别键:

1. 导入必要模块

from typing import List
from pydantic import BaseModel, DiscriminatedUnion

2. 保留子模型定义

保持原有的Ec2AssetSourceSimple和AssetSource不变:

class Ec2AssetSourceSimple(BaseModel):
    instanceType: str
    subnetId: str
    imageId: str
    groupName: str
    accountId: str
    macAddress: str
    reservationId: str
    monitoringEnabled: str
    spotInstance: str
    zone: str
    instanceState: str
    privateDnsName: str
    vpcId: str
    type: str
    availabilityZone: str
    privateIpAddress: str
    firstDiscovered: str
    ec2InstanceTags: dict
    publicIpAddress: str
    lastUpdated: str
    region: str
    assetId: int
    groupId: str
    localHostname: str
    publicDnsName: str

class AssetSource(BaseModel):
    pass

3. 定义互斥联合类型

创建联合类型,将每个子模型与对应的外层键绑定:

SourceInfoItem = DiscriminatedUnion(
    {
        "Ec2AssetSourceSimple": Ec2AssetSourceSimple,
        "AssetSource": AssetSource,
    }
)

4. 修改顶层模型

更新SourceInfo和QualysHost的字段类型:

class SourceInfo(BaseModel):
    list: List[SourceInfoItem]

class QualysHost(BaseModel):
    sourceInfo: SourceInfo

5. 验证数据

现在解析JSON时,Pydantic会自动识别每个列表项的类型,无需强制两个字段都存在:

json_data = {
    "sourceInfo": {
        "list": [
            {"Ec2AssetSourceSimple": {"instanceType": "t2.micro", ...}},
            {"AssetSource": {}}
        ]
    }
}

host = QualysHost(**json_data)
print(host.sourceInfo.list[0].Ec2AssetSourceSimple.instanceType)  # 输出 t2.micro

兼容Pydantic v1的替代方案

如果使用Pydantic v1,可通过root_validator实现字段互斥校验:

from typing import List, Optional
from pydantic import BaseModel, root_validator

class SourceInfoItem(BaseModel):
    Ec2AssetSourceSimple: Optional[Ec2AssetSourceSimple] = None
    AssetSource: Optional[AssetSource] = None

    @root_validator(pre=True)
    def check_exactly_one_field(cls, values):
        allowed_fields = ["Ec2AssetSourceSimple", "AssetSource"]
        present_fields = [k for k in values if k in allowed_fields]
        if len(present_fields) != 1:
            raise ValueError(f"必须且仅包含以下字段之一: {allowed_fields}")
        return values

    class Config:
        extra = "forbid"  # 禁止额外字段

class SourceInfo(BaseModel):
    list: List[SourceInfoItem]

class QualysHost(BaseModel):
    sourceInfo: SourceInfo

内容的提问来源于stack exchange,提问作者shluvme

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 05:34:57