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

如何为含$type无效语法键的JSON创建Pydantic计算字段?

解决Pydantic处理含$type字段JSON的问题

问题场景

现有如下JSON结构,其中包含Python无效变量名的$type字段:

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

当前Pydantic类能处理除$type外的所有字段,但需要严格遵循原JSON结构,保留$type字段的序列化/反序列化逻辑。

解决方案

利用Pydantic的计算字段(computed_field)、**别名(alias)特性,结合鉴别器(discriminator)**实现自动解析与序列化:

1. 定义基类处理$type字段

创建基类,通过计算字段自动生成$type值(类名),并指定别名匹配JSON中的$type键:

from pydantic import BaseModel, computed_field

class BaseMenuItem(BaseModel):
    # 计算字段,序列化时输出为$type,值为当前类名
    @computed_field(return_type=str, alias="$type")
    def _dollar_type(self) -> str:
        return self.__class__.__name__
    
    # 配置允许按别名或字段名赋值,兼容反序列化
    model_config = {"populate_by_name": True}

2. 定义子类并设置鉴别器

让所有业务类继承基类,同时为联合类型设置鉴别器,让Pydantic反序列化时自动根据$type匹配对应的类:

from typing import Annotated, Union
from pydantic import Field

class Pasta(BaseMenuItem):
    title: str

class Meat(BaseMenuItem):
    is_vegetable: bool

# 为联合类型设置鉴别器,基于$type字段解析子类
MenuItem = Annotated[Union[Pasta, Meat], Field(discriminator="$type")]

class Menu(BaseMenuItem):
    name: str
    children: list[MenuItem]

3. 测试序列化与反序列化

# 反序列化JSON到Pydantic对象
menu = Menu.model_validate(example)

# 序列化回JSON,保留$type字段
print(menu.model_dump_json(indent=2))

输出结果与原JSON结构一致:

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

关键说明

  • 计算字段+别名:通过computed_field的alias参数,将Python合法的字段名映射为JSON中的$type,序列化时自动输出该键;
  • 鉴别器:为联合类型设置discriminator="$type",让Pydantic反序列化时自动根据$type的值匹配对应的子类,无需手动处理类型判断;
  • populate_by_name:配置该选项后,Pydantic反序列化时会同时识别字段名和别名,避免因JSON中存在$type字段而报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 12:30:24