Pydantic V2多类型鉴别:序列化字段名与JSON结构不符的修复方法
问题描述
合法JSON结构1:
{ "foo": { "x": { "someField": 0 } } }
合法JSON结构2:
{ "foo": { "y": { "someDifferentField": "0" } } }
不合法JSON结构(foo不能同时包含x和y):
{ "foo": { "y": { "someDifferentField": "0" }, "x": { "someField": 0 } } }
使用以下Pydantic模型进行反序列化:
from pydantic import BaseModel, Field, Annotated, AliasChoices from typing import Union class SomeObject(BaseModel): pass class X(SomeObject): someField: int class Y(SomeObject): someDifferentField: str class Foo(BaseModel): """TODO.""" aObject: Annotated[ Union[X, Y], Field(validation_alias=AliasChoices("x", "y")), ]
反序列化流程正常,但调用foo.model_dump()时,输出结果中的键为aObject而非原JSON中的x或y:
{ "foo": { "aObject": { "someDifferentField": "0" } } }
或
{ "foo": { "aObject": { "someField": 0 } } }
需要修复序列化结果,使其输出原有的x或y键,而非aObject,且不能修改JSON文件结构。
解决方案
方法1:重写model_dump方法
直接在Foo类中重写model_dump方法,根据aObject的实际类型替换输出键:
class Foo(BaseModel): """TODO.""" aObject: Annotated[ Union[X, Y], Field(validation_alias=AliasChoices("x", "y")), ] def model_dump(self, **kwargs): # 调用父类方法获取基础序列化结果 base_dump = super().model_dump(**kwargs) # 取出aObject的值并删除原键 obj_data = base_dump.pop("aObject") # 根据类型添加对应的目标键 if isinstance(self.aObject, X): base_dump["x"] = obj_data elif isinstance(self.aObject, Y): base_dump["y"] = obj_data return base_dump
如果你的顶级模型包含foo字段(比如class RootModel(BaseModel): foo: Foo),序列化时会自动输出正确结构:
root = RootModel(foo=Foo(aObject=X(someField=0))) print(root.model_dump()) # 输出:{"foo": {"x": {"someField": 0}}}
方法2:使用字段序列化器(field_serializer)
通过Pydantic的field_serializer装饰器自定义序列化逻辑,同时隐藏原aObject字段:
from pydantic import field_serializer class Foo(BaseModel): """TODO.""" aObject: Annotated[ Union[X, Y], Field(validation_alias=AliasChoices("x", "y")), Field(exclude=True) # 序列化时排除原字段 ] @field_serializer("aObject", when_used="always") def serialize_aObject(self, value): if isinstance(value, X): return {"x": value.model_dump()} elif isinstance(value, Y): return {"y": value.model_dump()} def model_dump(self, **kwargs): # 合并序列化器结果与基础结果 serialized = super().model_dump(**kwargs) obj_serialized = self.serialize_aObject(self.aObject) serialized.update(obj_serialized) return serialized
关键注意点
- 确保
AliasChoices中的值与JSON键名大小写一致(示例中JSON用小写x/y,模型对应使用小写),避免反序列化匹配问题。 - 两种方法均不影响原有反序列化逻辑,仅调整序列化输出结构。
内容的提问来源于stack exchange,提问作者user15151532
相关产品推荐
相关产品推荐

