
在 pydantic 2 中,set 类型默认序列化为无序列表,导致 json 输出不稳定。本文介绍如何通过 `@field_serializer` 批量、声明式地将指定 set 字段自动转为排序后的 list,兼顾简洁性、可维护性与继承兼容性。
Pydantic 2 移除了 Pydantic 1 中的 json_encoders 配置,使得全局接管 set 序列化变得不直接。但其函数式序列化机制(@field_serializer)提供了更精准、更可控的替代方案——它允许你按字段名或类型模式声明序列化逻辑,且天然支持继承链中的字段。
最实用的做法是:为所有需排序的 set 字段显式添加 @field_serializer,并指定 when_used='json'(仅在 JSON 序列化时生效),避免影响 model_dump() 等其他导出行为。例如:
from typing import Set, Any
from pydantic import BaseModel, field_serializer
class BaseStudentModel(BaseModel):
name: str = 'Jane'
class StudentModel(BaseStudentModel):
courses: Set[str]
interests: Set[str]
# 单字段或多字段批量绑定同一序列化器
@field_serializer('courses', 'interests', when_used='json')
def sort_sets(self, value: Set[Any]) -> list:
return sorted(value)调用 StudentModel(courses={'Math', 'Chemistry'}, interests={'AI', 'Bio'}).model_dump_json() 将稳定输出:
{"name":"Jane","courses":["Chemistry","Math"],"interests":["AI","Bio"]}✅ 优势说明:
- ✅ 零侵入类型定义:无需自定义 SortedSet 类型或修改字段注解;
- ✅ 继承友好:子类可复用父类的 @field_serializer,也可在其基础上扩展新字段;
- ✅ 语义清晰:序列化逻辑与字段强关联,比 model_serializer 更易定位和维护;
- ✅ 性能高效:仅对目标字段执行 sorted(),无反射遍历开销。
⚠️ 注意事项:
- sorted() 要求集合元素可比较(如 str, int, datetime)。若含混合类型或不可比较对象(如 dict、自定义未实现 __lt__ 的类),需改用 sorted(value, key=str) 或自定义 key 函数;
- 若字段类型为泛型嵌套(如 Set[Union[int, str]]),建议在序列化器中添加类型检查或使用 isinstance(value, set) 增强健壮性;
- 不推荐在 when_used='always' 下使用该逻辑,否则会影响 model_dump() 返回的原生 Python 结构,违背“仅 JSON 有序”的设计初衷。
综上,@field_serializer 是 Pydantic 2 中实现 set → 排序 list 序列化的首选、标准且可持续演进的方案。它既尊重了框架的设计哲学(显式优于隐式、字段级控制优于全局钩子),又完美平衡了通用性与工程可维护性。






