![Pydantic 中 Json[Any] | str 联合类型的验证顺序详解](https://img.php.cn/upload/article/001/246/273/178330963314366.jpg)
Pydantic 默认对联合类型采用“智能模式”(smart mode),优先匹配最精确的类型(如字符串字面量直接匹配 str),导致 Json[Any] | str 中的 JSON 字符串被当作纯 str 处理;启用 union_mode="left_to_right" 可强制按声明顺序逐项尝试解析,使 JSON 字符串成功解析为 dict。
pydantic 默认对联合类型采用“智能模式”(smart mode),优先匹配最精确的类型(如字符串字面量直接匹配 `str`),导致 `json[any] | str` 中的 json 字符串被当作纯 `str` 处理;启用 `union_mode="left_to_right"` 可强制按声明顺序逐项尝试解析,使 json 字符串成功解析为 `dict`。
在 Pydantic v2 中,联合类型(Union)的验证行为由 union_mode 控制,默认为 "smart" —— 它会分析输入值的结构,选择语义上“最精确”的分支进行匹配。例如,当传入字符串 '{"a": 1}' 时,str 类型无需任何转换即可完全匹配,而 Json[Any] 需要反序列化,因此 smart mode 优先选择 str,导致本应解析为字典的 JSON 字符串被原样保留为 str。
但注意:Json[Any] | int 表现出不同行为(如示例中返回 dict),这是因为 int 无法直接匹配 JSON 字符串('{"a": 1}' 不是有效整数),于是 fallback 到 Json[Any] 并成功解析。这凸显了 smart mode 的“精确性优先”逻辑:它并非简单按顺序尝试,而是基于类型兼容性做启发式判断。
✅ 正确解决方案:显式指定 union_mode="left_to_right"
该模式严格遵循类型注解的声明顺序,依次尝试每个选项,一旦某分支验证成功即停止。对于 Json[Any] | str,它会先尝试 Json[Any] —— 若输入是合法 JSON 字符串,则解析为 Python 对象(如 dict);仅当解析失败时,才尝试匹配 str。
from typing import Any
from pydantic import BaseModel, Field, Json
class FooStr(BaseModel):
json_or_str: Json[Any] | str = Field(union_mode="left_to_right")
# ✅ 现在 JSON 字符串被正确解析
instance = FooStr(json_or_str='{"a": 1}')
print(type(instance.json_or_str)) # <class 'dict'>
print(instance.json_or_str) # {'a': 1}
# ✅ 非 JSON 字符串仍可作为 str 接收
instance2 = FooStr(json_or_str="hello")
print(type(instance2.json_or_str)) # <class 'str'>
print(instance2.json_or_str) # hello⚠️ 注意事项:
- union_mode 必须通过 Field() 设置,不能直接写在类型注解中;
- Json[T] 字段要求输入为 字符串形式的 JSON(如 '{"key": "value"}'),若传入已解析的 dict 或 list,会因类型不匹配而报错;
- 若需同时支持原始字符串、JSON 字符串和已解析对象,建议使用自定义 validator 或拆分为独立字段,避免过度依赖 union 的隐式行为;
- 在 Pydantic v2.7+ 中,union_mode 也可在模型配置中全局设置(model_config = ConfigDict(union_mode='left_to_right')),但字段级 Field 设置优先级更高。
总之,理解 Pydantic 联合类型的验证策略是设计健壮数据模型的关键。当语义意图明确要求“先尝试 JSON 解析,失败再当字符串处理”时,union_mode="left_to_right" 是简洁、可靠且符合直觉的选择。


















