如何为含$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
相关产品推荐
相关产品推荐

