Pydantic序列化终极指南:Python对象到JSON的完美转换
1. 序列化痛点与解决方案
你是否曾遇到过Python对象转JSON的困境?日期格式混乱、自定义类型无法序列化、嵌套对象处理复杂?Pydantic(数据验证工具,使用Python类型提示)提供了一套完整的序列化解决方案,让Python对象到JSON的转换变得简单高效。本文将深入探讨Pydantic的序列化机制,从基础用法到高级技巧,帮助你掌握Python对象到JSON的完美转换。
读完本文,你将能够:
- 熟练使用Pydantic的序列化方法(
model_dump和model_dump_json) - 定制字段的序列化行为
- 处理复杂类型(日期、枚举、嵌套模型等)的序列化
- 优化序列化性能
- 解决常见的序列化问题
2. Pydantic序列化基础
2.1 核心序列化方法
Pydantic提供了两种主要的序列化方法:model_dump和model_dump_json。这两个方法都定义在BaseModel类中,是Pydantic模型序列化的基础。
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
age: int | None = None
user = User(id=1, name="John Doe", age=30)
# 序列化为Python字典
user_dict = user.model_dump()
print(user_dict)
# 输出: {'id': 1, 'name': 'John Doe', 'age': 30}
# 直接序列化为JSON字符串
user_json = user.model_dump_json()
print(user_json)
# 输出: {"id":1,"name":"John Doe","age":30}
2.2 序列化方法参数详解
model_dump和model_dump_json提供了丰富的参数,用于定制序列化行为。以下是一些常用参数:
| 参数名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| include | IncEx | None | None | 指定要包含的字段 |
| exclude | IncEx | None | None | 指定要排除的字段 |
| by_alias | bool | None | None | 是否使用字段别名 |
| exclude_unset | bool | False | 是否排除未显式设置的字段 |
| exclude_defaults | bool | False | 是否排除设置为默认值的字段 |
| exclude_none | bool | False | 是否排除值为None的字段 |
下面是一些使用示例:
# 排除age字段
user_dict = user.model_dump(exclude={"age"})
print(user_dict)
# 输出: {'id': 1, 'name': 'John Doe'}
# 只包含id字段
user_dict = user.model_dump(include={"id"})
print(user_dict)
# 输出: {'id': 1}
# 排除未显式设置的字段(假设age是默认值)
user = User(id=1, name="John Doe")
user_dict = user.model_dump(exclude_unset=True)
print(user_dict)
# 输出: {'id': 1, 'name': 'John Doe'} (如果age有默认值None,则会被排除)
# 排除值为None的字段
user = User(id=1, name="John Doe", age=None)
user_dict = user.model_dump(exclude_none=True)
print(user_dict)
# 输出: {'id': 1, 'name': 'John Doe'}
3. 字段级别的序列化控制
3.1 使用Field定制序列化行为
Pydantic的Field函数提供了多种参数,可以在字段级别控制序列化行为。
from pydantic import BaseModel, Field
class User(BaseModel):
id: int
name: str = Field(alias='username')
age: int | None = Field(default=None, exclude=True)
user = User(id=1, username="John Doe", age=30)
# 使用别名序列化
user_dict = user.model_dump(by_alias=True)
print(user_dict)
# 输出: {'id': 1, 'username': 'John Doe'}
# age字段被排除
print('age' in user_dict) # 输出: False
3.2 自定义序列化函数
对于复杂的序列化需求,可以使用@field_serializer装饰器定义自定义序列化函数。
from pydantic import BaseModel, field_serializer
from datetime import datetime
class Event(BaseModel):
name: str
timestamp: datetime
@field_serializer('timestamp')
def serialize_timestamp(self, value: datetime) -> str:
return value.strftime('%Y-%m-%d %H:%M:%S')
event = Event(name="Conference", timestamp=datetime(2023, 10, 5, 9, 30))
print(event.model_dump())
# 输出: {'name': 'Conference', 'timestamp': '2023-10-05 09:30:00'}
4. 模型级别的序列化控制
4.1 配置模型序列化行为
通过model_config可以在模型级别配置默认的序列化行为。
from pydantic import BaseModel, ConfigDict
class User(BaseModel):
model_config = ConfigDict(
by_alias=True,
exclude_unset=False,
exclude_defaults=False,
exclude_none=True
)
id: int
name: str = Field(alias='username')
age: int | None = None
user = User(id=1, username="John Doe", age=None)
print(user.model_dump())
# 输出: {'id': 1, 'username': 'John Doe'} (age为None被排除,使用了别名)
4.2 模型序列化器
使用@model_serializer装饰器可以定义整个模型的自定义序列化逻辑。
from pydantic import BaseModel, model_serializer
class User(BaseModel):
id: int
name: str
age: int
@model_serializer
def serialize_model(self) -> dict[str, any]:
return {
'user_id': self.id,
'user_name': self.name,
'is_adult': self.age >= 18
}
user = User(id=1, name="John Doe", age=30)
print(user.model_dump())
# 输出: {'user_id': 1, 'user_name': 'John Doe', 'is_adult': True}
5. 处理复杂类型
5.1 嵌套模型
Pydantic会自动处理嵌套模型的序列化。
from pydantic import BaseModel
class Address(BaseModel):
street: str
city: str
class User(BaseModel):
name: str
address: Address
user = User(
name="John Doe",
address=Address(street="123 Main St", city="Anytown")
)
print(user.model_dump())
# 输出: {'name': 'John Doe', 'address': {'street': '123 Main St', 'city': 'Anytown'}}
5.2 集合类型
Pydantic支持各种集合类型的序列化,包括列表、字典、集合等。
from pydantic import BaseModel
from typing import List, Dict, Set
class Data(BaseModel):
numbers: List[int]
info: Dict[str, str]
tags: Set[str]
data = Data(
numbers=[1, 2, 3],
info={'a': 'apple', 'b': 'banana'},
tags={'python', 'pydantic'}
)
print(data.model_dump())
# 输出: {'numbers': [1, 2, 3], 'info': {'a': 'apple', 'b': 'banana'}, 'tags': {'python', 'pydantic'}}
5.3 枚举类型
Pydantic可以序列化枚举类型,默认使用枚举值。
from pydantic import BaseModel
from enum import Enum
class Status(Enum):
PENDING = 'pending'
COMPLETE = 'complete'
class Task(BaseModel):
name: str
status: Status
task = Task(name="Learn Pydantic", status=Status.COMPLETE)
print(task.model_dump())
# 输出: {'name': 'Learn Pydantic', 'status': <Status.COMPLETE: 'complete'>}
# 使用枚举值序列化
print(task.model_dump(mode='json'))
# 输出: {'name': 'Learn Pydantic', 'status': 'complete'}
6. 高级序列化技巧
6.1 条件序列化
可以根据模型的状态动态决定是否包含某些字段。
from pydantic import BaseModel, field_serializer
class Order(BaseModel):
id: int
status: str
total: float
discount_code: str | None = None
@field_serializer('discount_code', when_used='unless-none')
def serialize_discount(self, value: str) -> str:
return value
order1 = Order(id=1, status='completed', total=99.99)
order2 = Order(id=2, status='completed', total=49.99, discount_code='SAVE10')
print(order1.model_dump())
# 输出: {'id': 1, 'status': 'completed', 'total': 99.99}
print(order2.model_dump())
# 输出: {'id': 2, 'status': 'completed', 'total': 49.99, 'discount_code': 'SAVE10'}
6.2 递归模型序列化
对于递归结构的模型,可以使用model_rebuild方法确保正确序列化。
from pydantic import BaseModel
from typing import List, Optional
class Category(BaseModel):
id: int
name: str
subcategories: Optional[List['Category']] = None
# 重建模型以处理前向引用
Category.model_rebuild()
electronics = Category(
id=1,
name='Electronics',
subcategories=[
Category(id=2, name='Smartphones'),
Category(id=3, name='Laptops')
]
)
print(electronics.model_dump())
# 输出: {'id': 1, 'name': 'Electronics', 'subcategories': [{'id': 2, 'name': 'Smartphones', 'subcategories': None}, {'id': 3, 'name': 'Laptops', 'subcategories': None}]}
6.3 使用TypeAdapter进行非模型类型序列化
对于非模型类型(如基本类型、数据类等),可以使用TypeAdapter进行序列化。
from pydantic import TypeAdapter
from typing import List, Dict
# 序列化列表
adapter = TypeAdapter(List[int])
print(adapter.dump_python([1, 2, 3])) # 输出: [1, 2, 3]
# 序列化字典
adapter = TypeAdapter(Dict[str, int])
print(adapter.dump_python({'a': 1, 'b': 2})) # 输出: {'a': 1, 'b': 2}
7. 序列化性能优化
7.1 序列化模式对比
Pydantic提供了两种主要的序列化模式:python和json。python模式将模型转换为Python原生类型,json模式直接生成JSON字符串。
from pydantic import BaseModel
import timeit
class LargeData(BaseModel):
numbers: list[int]
data: dict[str, str]
# 创建大型模型实例
large_data = LargeData(
numbers=list(range(1000)),
data={f'key_{i}': f'value_{i}' for i in range(1000)}
)
# 测量model_dump性能
def dump_python():
large_data.model_dump()
# 测量model_dump_json性能
def dump_json():
large_data.model_dump_json()
print(f"model_dump: {timeit.timeit(dump_python, number=1000):.2f}s")
print(f"model_dump_json: {timeit.timeit(dump_json, number=1000):.2f}s")
通常情况下,model_dump_json比model_dump+json.dumps更高效,因为它直接使用Pydantic的优化序列化器。
7.2 排除不必要的字段
序列化时排除不必要的字段可以显著提高性能,特别是对于大型模型。
# 排除大型字段
result = large_data.model_dump(exclude={'numbers'})
7.3 使用Pydantic Core
Pydantic 2.0+基于Pydantic Core(一个用Rust编写的高性能核心),比早期版本快得多。确保使用最新版本的Pydantic以获得最佳性能。
8. 常见序列化问题及解决方案
8.1 循环引用
循环引用会导致序列化失败,可使用exclude参数或自定义序列化器解决。
from pydantic import BaseModel
class Node(BaseModel):
id: int
next: 'Node' | None = None
Node.model_rebuild()
# 创建循环引用
node1 = Node(id=1)
node2 = Node(id=2, next=node1)
node1.next = node2 # 循环引用
# 尝试序列化会导致错误
try:
node1.model_dump()
except ValueError as e:
print(f"Error: {e}")
# 解决方案:排除循环引用字段
print(node1.model_dump(exclude={'next'})) # 输出: {'id': 1}
8.2 非JSON可序列化类型
对于非JSON可序列化类型,需要提供自定义序列化函数。
from pydantic import BaseModel, field_serializer
from datetime import timedelta
class Duration(BaseModel):
delta: timedelta
@field_serializer('delta')
def serialize_delta(self, value: timedelta) -> float:
return value.total_seconds()
duration = Duration(delta=timedelta(hours=2, minutes=30))
print(duration.model_dump_json())
# 输出: {"delta":9000.0}
8.3 处理敏感数据
可以使用自定义序列化器对敏感数据进行脱敏处理。
from pydantic import BaseModel, field_serializer
class User(BaseModel):
id: int
name: str
password: str
@field_serializer('password')
def serialize_password(self, value: str) -> str:
return '***' # 脱敏处理
user = User(id=1, name="John Doe", password="secret123")
print(user.model_dump())
# 输出: {'id': 1, 'name': 'John Doe', 'password': '***'}
9. 总结与展望
Pydantic提供了强大而灵活的序列化功能,从简单的对象到JSON转换,到复杂的自定义序列化需求,都能轻松应对。通过本文介绍的方法,你可以:
- 使用
model_dump和model_dump_json进行基本序列化 - 通过
Field和序列化装饰器定制序列化行为 - 处理嵌套模型、集合、枚举等复杂类型
- 应用高级技巧如条件序列化和递归模型处理
- 优化序列化性能
- 解决常见的序列化问题
随着Pydantic的不断发展,未来还会有更多强大的序列化功能。建议保持关注Pydantic的官方文档和更新日志,以获取最新信息。
掌握Pydantic序列化不仅能提高代码质量和性能,还能让你轻松处理各种复杂的数据转换场景。现在就开始在你的项目中应用这些技巧,体验Pydantic带来的便利吧!
希望本文对你有所帮助,如果你有任何问题或建议,请在评论区留言。别忘了点赞、收藏和关注,以获取更多关于Pydantic和Python开发的优质内容!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



