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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 07:43:17