pin_drop当前位置:知识文库 ❯ 图文
JSONEncoder详解 - Python自定义JSON序列化完整指南
概述
json.JSONEncoder 是 json 模块中用于 JSON 编码的基类,它定义了将 Python 对象序列化为 JSON 的默认行为。通过继承 JSONEncoder 并重写 default() 方法,可以扩展编码器以支持自定义类型(如 datetime、Decimal、自定义类等),实现可复用的序列化逻辑。JSONEncoder 适用于项目中需要统一处理特定类型序列化的场景。
语法
代码示例
import json
class CustomEncoder(json.JSONEncoder):
def default(self, obj):
# 自定义序列化逻辑
if isinstance(obj, SomeType):
return convertible_value
return super().default(obj)
# 使用自定义编码器
json_str = json.dumps(data, cls=CustomEncoder)参数说明
JSONEncoder构造函数
需要重写的方法
返回值
JSONEncoder 的 encode() 方法返回 JSON 字符串 str。iterencode() 方法返回生成器,逐部分生成 JSON 字符串。
代码示例
示例1:支持datetime的自定义编码器
代码示例
import json
from datetime import datetime, date
class DateTimeEncoder(json.JSONEncoder):
"""支持datetime和date的编码器"""
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
elif isinstance(obj, date):
return obj.isoformat()
return super().default(obj)
# 使用自定义编码器
data = {
"event": "会议",
"created_at": datetime(2024, 1, 15, 14, 30, 0),
"deadline": date(2024, 6, 30),
}
result = json.dumps(data, cls=DateTimeEncoder, ensure_ascii=False, indent=2)
print(result)输出:
代码示例
{
"event": "会议",
"created_at": "2024-01-15T14:30:00",
"deadline": "2024-06-30"
}示例2:多类型支持编码器
代码示例
import json
from decimal import Decimal
from datetime import datetime, date
from enum import Enum
class UniversalEncoder(json.JSONEncoder):
"""通用多类型编码器"""
def default(self, obj):
if isinstance(obj, datetime):
return {"__datetime__": obj.isoformat()}
elif isinstance(obj, date):
return {"__date__": obj.isoformat()}
elif isinstance(obj, Decimal):
return {"__decimal__": str(obj)}
elif isinstance(obj, Enum):
return {"__enum__": obj.value}
elif isinstance(obj, set):
return {"__set__": list(obj)}
elif isinstance(obj, bytes):
return {"__bytes__": obj.decode('utf-8')}
return super().default(obj)
# 测试多种类型
class Status(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
data = {
"price": Decimal("19.99"),
"status": Status.ACTIVE,
"tags": {"python", "json"},
"data": b"hello",
}
result = json.dumps(data, cls=UniversalEncoder, ensure_ascii=False, indent=2)
print(result)输出:
代码示例
{
"price": {
"__decimal__": "19.99"
},
"status": {
"__enum__": "active"
},
"tags": {
"__set__": [
"python",
"json"
]
},
"data": {
"__bytes__": "hello"
}
}示例3:自定义对象序列化
代码示例
import json
class User:
"""自定义用户类"""
def __init__(self, name, age, email):
self.name = name
self.age = age
self.email = email
def to_dict(self):
return {"name": self.name, "age": self.age, "email": self.email}
class UserEncoder(json.JSONEncoder):
"""用户对象编码器"""
def default(self, obj):
if isinstance(obj, User):
return {"__user__": obj.to_dict()}
return super().default(obj)
# 使用
user = User("张三", 25, "zhangsan@example.com")
data = {
"user": user,
"message": "用户注册成功",
}
result = json.dumps(data, cls=UserEncoder, ensure_ascii=False, indent=2)
print(result)输出:
代码示例
{
"user": {
"__user__": {
"name": "张三",
"age": 25,
"email": "zhangsan@example.com"
}
},
"message": "用户注册成功"
}实际应用场景
-
统一序列化策略:在项目中定义一个全局编码器,统一处理 datetime、Decimal 等类型的序列化
-
自定义对象序列化:为业务对象定义 JSON 表示方式,实现对象到 JSON 的自动转换
-
API 响应格式化:在 Web 框架中注册自定义编码器,统一处理响应数据的序列化
注意事项
注意1:重写
default()方法时,对于无法处理的类型必须调用super().default(obj),否则会丢失默认的错误处理逻辑。
注意2:
default()方法返回的值必须是 JSON 可序列化的类型(dict、list、str、int、float、bool、None),否则会递归调用default()。
注意3:使用类型标记(如
__datetime__)时,需确保对应的解码器能识别这些标记,否则反序列化后类型信息会丢失。
提示:
JSONEncoder的encode()和iterencode()方法可以直接调用,无需通过json.dumps()。iterencode()适合处理大型数据,逐块生成 JSON。
相关方法对比
小结
-
JSONEncoder通过继承和重写default()方法实现自定义类型的序列化 -
始终在
default()末尾调用super().default(obj)处理未知类型 -
使用类型标记(如
__datetime__)可以在解码时恢复原始类型 -
项目中推荐定义统一的编码器类,避免在每次
dumps()时重复指定default参数
练习题
练习1
编写一个 ComplexEncoder,支持序列化 Python 的 complex 复数类型,格式为 {"__complex__": [real, imag]}。
练习2
编写配套的 complex_decoder 函数(作为 object_hook),实现复数类型的完整序列化/反序列化循环。
练习3
编写一个 DataclassEncoder,自动将 @dataclass 装饰的类实例序列化为字典,使用 dataclasses.asdict() 实现。
常见问题
如何创建支持datetime的JSON编码器?
继承json.JSONEncoder类并重写default()方法,在方法中检查对象是否为datetime或date类型,如果是则调用isoformat()方法转换为字符串格式。
为什么要在default()方法末尾调用super().default(obj)?
调用super().default(obj)可以处理未知的类型,抛出标准的TypeError异常。如果不这样做,遇到无法处理的类型时会导致不可预期的行为。
什么是类型标记?有什么优缺点?
类型标记是在序列化时使用特殊键名(如__datetime__)标记数据类型的方法。优点是可以完整保留类型信息用于反序列化;缺点是不是JSON标准做法,与其他工具不兼容。
JSONEncoder和default参数有什么区别?
JSONEncoder子类是可复用的类,可以在项目多个地方使用;default参数适合临时使用,每次调用dumps()都需要传入。项目级统一处理推荐使用JSONEncoder子类。
本文涉及AI创作
内容由AI创作,请仔细甄别