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

如何构建序列化时不产生嵌套结构的Pydantic模型?

如何构建序列化时不产生嵌套结构的Pydantic模型?

嘿,这个需求我之前也碰到过!既要让Foo模型保留数据验证能力,又不想在序列化Bar时出现嵌套结构,直接输出原始的字符串值对吧?用Pydantic自带的工具就能轻松实现,给你两个完全匹配你需求的方案:

方案一:轻量自定义类型(适合简单验证场景)

如果你的Foo只需要做基础的字符串验证,不需要复杂的模型结构,用Annotated结合验证器和序列化器是最简洁的方式:

from pydantic import BaseModel, field_validator, PlainSerializer, WithJsonSchema
from typing import Annotated

# 定义带验证的自定义类型Foo,直接基于字符串扩展
Foo = Annotated[
    str,
    # 这里写你的验证逻辑,比如检查字符串长度至少3位
    field_validator(lambda v: len(v) >= 3 or ValueError("字符串长度不能少于3位")),
    # 指定序列化时直接输出原始字符串,避免嵌套
    PlainSerializer(lambda x: x, return_type=str),
    WithJsonSchema({"type": "string"})
]

class Bar(BaseModel):
    bar: Foo

# 完全符合你的测试用例
y = Bar(bar="xxx")  # 这里甚至可以直接传字符串,Pydantic会自动转成Foo类型
print(y.json())  # 输出: {"bar": "xxx"}

# 验证功能正常触发,传入不符合要求的值会报错
try:
    Bar(bar="xx")
except Exception as e:
    print(e)  # 抛出"字符串长度不能少于3位"的验证错误

方案二:基于BaseModel的Foo(适合复杂验证/结构场景)

如果你需要Foo是一个正经的BaseModel子类,或者有更复杂的验证逻辑,那可以用__root__字段支持直接传字符串实例化,再给Bar的字段加个序列化器:

from pydantic import BaseModel, field_validator, field_serializer

class Foo(BaseModel):
    __root__: str  # 用__root__字段,支持直接用Foo('xxx')实例化

    # 自定义验证逻辑,比如检查字符串必须包含字母
    @field_validator("__root__")
    def check_has_letter(cls, v):
        if not any(c.isalpha() for c in v):
            raise ValueError("字符串必须包含至少一个字母")
        return v

class Bar(BaseModel):
    bar: Foo

    # 序列化时直接取出Foo的核心值,避免嵌套
    @field_serializer("bar")
    def serialize_bar(self, foo_instance: Foo) -> str:
        return foo_instance.__root__

# 完美匹配你的测试代码
y = Bar(bar=Foo('xxx'))
print(y.json())  # 输出: {"bar": "xxx"}

# 验证逻辑正常工作
try:
    Bar(bar=Foo('123'))
except Exception as e:
    print(e)  # 抛出"字符串必须包含至少一个字母"的错误

这两个方案都能满足你的要求:Foo负责数据验证,序列化Bar时直接输出原始字符串,完全没有嵌套结构。你可以根据自己的实际需求选,简单验证用方案一,复杂场景用方案二就行~

备注:内容来源于stack exchange,提问作者nipil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 14:24:35