← 返回Python标准库精讲目录
← 返回学习笔记首页
专题: Python标准库精讲系统学习
关键词: Python, 标准库, json, JSON, dumps, loads, dump, load, JSONEncoder, JSONDecoder, 序列化
一、JSON格式概述
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,广泛用于Web应用、配置文件、API通信等场景。Python的json模块提供了完整的JSON编码(序列化)和解码(反序列化)功能,能够在Python对象和JSON字符串之间灵活转换。
理解Python数据类型与JSON数据类型的对应关系,是正确使用json模块的基础。下表列出了二者之间的标准映射规则:
Python类型
JSON类型
示例
说明
dict
object
{"key": "value"}
键必须为字符串类型
list, tuple
array
[1, 2, 3]
tuple编码后变为list
str
string
"hello"
必须使用双引号
int, float, int子类
number
42, 3.14
float('inf')会引发异常
True / False
true / false
true, false
注意大小写差异
None
null
null
Python中的None直接映射
Decimal
不支持原生
需自定义编码器
默认会报TypeError
datetime
不支持原生
需自定义编码器
通常转为ISO格式字符串
从表中可以看出,Python的基本数据类型(dict、list、str、int、float、bool、None)可以与JSON自然对应,但Decimal、datetime、自定义类等类型则需要额外处理——这正是后续章节自定义编码器要解决的问题。
核心认知: JSON格式本身仅支持有限的数据类型,所有复杂Python对象都必须"降维"为这些基本类型的组合才能完成序列化。理解这个约束,就能理解为什么需要自定义编码器。
二、核心编码函数
json模块提供了两个核心编码函数:json.dumps()用于将Python对象转换为JSON字符串,json.dump()用于将Python对象直接写入文件流。二者在参数上完全一致,区别仅在于输出目标不同。
2.1 json.dumps() 参数详解
json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw) 是JSON序列化的核心函数。以下逐一讲解最常用的参数及其实战用法:
sort_keys — 排序输出
设置sort_keys=True可以让输出JSON的键按字母顺序排列。这在版本控制对比、缓存键生成等场景中非常有用,能够保证相同内容始终产生相同的输出顺序。
import json
data = {"name": "Alice", "age": 30, "role": "admin"}
result = json.dumps(data, sort_keys=True, indent=2)
print(result)
# 输出:
# {
# "age": 30,
# "name": "Alice",
# "role": "admin"
# }
indent — 格式化缩进
indent参数控制输出的缩进空格数,使JSON对人类可读。indent=2使用2个空格缩进,indent=4使用4个空格缩进。该参数在调试日志、配置文件生成场景中不可或缺。
# indent为None(默认)时输出紧凑格式
print(json.dumps({"a": 1, "b": 2}))
# 输出:{"a": 1, "b": 2}
# indent=2输出格式化格式
print(json.dumps({"a": 1, "b": 2}, indent=2))
# 输出:
# {
# "a": 1,
# "b": 2
# }
separators — 紧凑控制
separators参数接受一个二元组(item_separator, key_separator),用于控制输出中各项之间的分隔符。默认值为(', ', ': '),可通过设置为(',', ':')来移除多余空格,生成最小体积的JSON,适用于网络传输场景。
data = {"name": "Bob", "scores": [90, 85, 92]}
compact = json.dumps(data, separators=(',', ':'))
print(compact)
# 输出:{"name":"Bob","scores":[90,85,92]}
# 对比默认输出
default = json.dumps(data)
print(default)
# 输出:{"name": "Bob", "scores": [90, 85, 92]}
ensure_ascii — ASCII转义控制
ensure_ascii参数控制非ASCII字符(如中文)的处理方式。默认为True,会将所有非ASCII字符转义为\\uXXXX形式。设置为False时,保留原字符,使JSON内容可读性大幅提升。此参数在中文处理章节会详细展开。
data = {"message": "你好世界"}
print(json.dumps(data))
# 输出:{"message": "\\u4f60\\u597d\\u4e16\\u754c"}
print(json.dumps(data, ensure_ascii=False))
# 输出:{"message": "你好世界"}
skipkeys — 键类型容错
默认情况下,如果字典键不是基本类型(str、int、float、bool、None),json.dumps会抛出TypeError。设置skipkeys=True可以跳过这些非标准键,而不是抛出异常。这在处理混合类型键的字典时非常实用。
data = {("tuple_key",): "value", "normal_key": 42}
# 默认行为:TypeError: keys must be str, int, float, bool or None
# 使用skipkeys跳过不可序列化的键
result = json.dumps(data, skipkeys=True)
print(result)
# 输出:{"normal_key": 42}
default — 兜底序列化函数
default参数接受一个函数,当json模块遇到无法序列化的对象时,会调用此函数进行处理。该函数接收无法序列化的对象,应返回一个可序列化的表示形式。这是快速处理自定义类型的最简便方式。
from datetime import datetime
def custom_serializer(obj):
if isinstance(obj, datetime):
return obj.isoformat()
raise TypeError(f"Type {type(obj)} not serializable")
now = datetime.now()
data = {"event": "meeting", "time": now}
result = json.dumps(data, default=custom_serializer)
print(result)
# 输出示例:{"event": "meeting", "time": "2026-05-05T23:56:48.123456"}
2.2 json.dump() — 写入文件流
json.dump()用于将Python对象直接序列化并写入文件对象。其参数与dumps()完全一致,区别在于第一个参数之后需要传入一个可写的文件对象。务必确保文件以文本模式打开并指定正确的编码。
import json
data = {"name": "配置文件", "version": "2.0", "debug": False}
# 写入JSON文件
with open("config.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
# 读取验证
with open("config.json", "r", encoding="utf-8") as f:
loaded = json.load(f)
print(loaded)
# 输出:{"name": "配置文件", "version": "2.0", "debug": False}
注意: 使用dump写入文件时,必须显式指定encoding="utf-8"(Python 3默认已使用UTF-8,但显式指定是良好实践)。另外注意使用w模式而非wb模式——JSON是文本格式,不应以二进制模式写入。
三、核心解码函数
json模块提供了两个核心解码函数:json.loads()用于将JSON字符串解析为Python对象,json.load()用于从文件流中读取并解析JSON数据。二者参数基本一致,可以处理相同的JSON格式选项。
3.1 json.loads() — 解析字符串
json.loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw) 是最常用的解码函数。它将JSON字符串严格解析为对应的Python对象,默认情况下不接受单引号、注释等非标准JSON格式。
import json
# 基本用法
json_str = '{"name": "Alice", "age": 30, "scores": [95, 87, 92]}'
data = json.loads(json_str)
print(data)
# 输出:{"name": "Alice", "age": 30, "scores": [95, 87, 92]}
print(type(data))
# 输出:
# 数组解析为列表
arr_str = '[1, 2, 3, "four", true, null]'
arr = json.loads(arr_str)
print(arr)
# 输出:[1, 2, 3, "four", True, None]
严格性: json.loads遵循严格的JSON规范(RFC 7159),不支持Python字面量语法中的单引号、尾随逗号、注释等扩展语法。如果需要解析更宽松的格式,可以考虑使用ast.literal_eval或第三方库(如demjson)。
# 以下用法会引发异常:
# json.loads("{'name': 'Alice'}") # SyntaxError: 单引号不被允许
# json.loads('{"name": "Alice",}') # SyntaxError: 尾随逗号不被允许
# json.loads('{"name": /*comment*/ "Alice"}') # SyntaxError: 注释不被允许
3.2 json.load() — 从文件流读取
json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw) 从文件对象中读取JSON数据并解析。它是对loads的封装,内部会自动读取文件内容再调用loads。务必以文本模式打开文件。
import json
# 准备示例文件
data = {"projects": [{"id": 1, "name": "Alpha"}, {"id": 2, "name": "Beta"}]}
with open("projects.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
# 从文件加载
with open("projects.json", "r", encoding="utf-8") as f:
loaded = json.load(f)
print(loaded["projects"][0]["name"])
# 输出:Alpha
性能要点: 对于大文件(数百MB以上),load()会将整个文件读入内存再解析,可能占用大量内存。如需处理超大JSON文件,建议使用ijson等流式解析库,可以逐层解析而不将全部数据加载到内存中。
3.3 解析钩子函数
loads和load支持parse_float、parse_int、parse_constant三个参数,用于拦截JSON基本类型的解析过程。这在需要特殊数字精度控制(如将浮点数全部解析为Decimal)的场景中非常实用。
from decimal import Decimal
json_str = '{"price": 19.99, "quantity": 100, "large": 1e20}'
data = json.loads(
json_str,
parse_float=Decimal,
parse_int=lambda x: int(x) * 2 # 整数翻倍
)
print(data["price"], type(data["price"]))
# 输出:19.99
print(data["quantity"], data["large"])
# 输出:200 1E+20
四、自定义编码器
当默认的json.dumps无法处理自定义Python对象时,我们需要创建JSONEncoder的子类并覆盖default()方法。这是官方推荐的自定义序列化方案,具有可复用、封装性好、与框架集成度高等优点。
4.1 基础自定义编码器
继承json.JSONEncoder并实现default(self, o)方法,该方法接收一个无法被默认编码器处理的对象o,应返回一个可被JSON序列化的Python对象。如果仍无法处理,则应调用super().default(o)以触发默认的TypeError异常。
import json
from datetime import datetime, date
from decimal import Decimal
class CustomEncoder(json.JSONEncoder):
"""支持datetime、date、Decimal的自定义JSON编码器"""
def default(self, o):
if isinstance(o, datetime):
return o.strftime("%Y-%m-%d %H:%M:%S")
if isinstance(o, date):
return o.isoformat()
if isinstance(o, Decimal):
return float(o) # 注意:可能有精度损失
if isinstance(o, set):
return list(o)
if isinstance(o, bytes):
return o.decode("utf-8", errors="replace")
return super().default(o)
# 使用自定义编码器
data = {
"name": "事件记录",
"created_at": datetime.now(),
"event_date": date(2026, 5, 5),
"score": Decimal("99.95"),
"tags": {"urgent", "important"},
"raw": b"hello bytes"
}
# 方式一:通过cls参数传入
result = json.dumps(data, cls=CustomEncoder, indent=2, ensure_ascii=False)
print(result)
# 输出:
# {
# "name": "事件记录",
# "created_at": "2026-05-05 23:56:48",
# "event_date": "2026-05-05",
# "score": 99.95,
# "tags": ["urgent", "important"],
# "raw": "hello bytes"
# }
4.2 使用inheritence链式编码
在某些复杂场景中,需要将多种自定义编码器组合使用。可以通过Mixin或继承链的方式实现编码器的复用。下面展示一个结合了多种特殊类型支持的编码器:
import json
from pathlib import Path
from uuid import UUID
class AdvancedEncoder(json.JSONEncoder):
"""支持更多Python特殊类型的编码器"""
def default(self, o):
if isinstance(o, UUID):
return str(o)
if isinstance(o, Path):
return str(o.as_posix())
if isinstance(o, complex):
return {"real": o.real, "imag": o.imag}
if isinstance(o, range):
return list(o)
if isinstance(o, frozenset):
return list(o)
if isinstance(o, Exception):
return {"type": type(o).__name__, "args": o.args}
# 尝试调用对象的to_dict方法(如果有)
if hasattr(o, "to_dict") and callable(o.to_dict):
return o.to_dict()
return super().default(o)
# 配合前面的CustomEncoder实现链式
class ChainEncoder(CustomEncoder, AdvancedEncoder):
"""组合两个编码器的能力"""
pass
# 测试
from uuid import uuid4
test_data = {
"id": uuid4(),
"path": Path("/usr/local/config"),
"fp": frozenset([1, 2, 3])
}
print(json.dumps(test_data, cls=ChainEncoder, indent=2))
# {
# "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
# "path": "/usr/local/config",
# "fp": [1, 2, 3]
# }
4.3 迭代器与生成器编码
JSONEncoder默认支持list、tuple等序列类型,但无法直接处理迭代器和生成器。通过在default方法中处理这些类型,可以实现流式数据的编码:
class IterableEncoder(json.JSONEncoder):
def default(self, o):
try:
if hasattr(o, "__iter__"):
return list(o)
except TypeError:
pass
return super().default(o)
def number_generator(n):
for i in range(n):
yield i
data = {"numbers": number_generator(5)}
result = json.dumps(data, cls=IterableEncoder)
print(result)
# 输出:{"numbers": [0, 1, 2, 3, 4]}
最佳实践: 自定义JSONEncoder通常比使用dumps的default参数更可取。原因有三:一是编码器可复用,一处定义到处使用;二是子类可以覆盖iterencode等方法实现更细粒度的控制;三是与Flask、Django等框架的jsonify集成更自然。
五、自定义解码器
与编码对应,json模块也提供了自定义解码的机制。通过继承JSONDecoder或利用object_hook参数,可以控制JSON数据反序列化为Python对象的过程,实现从普通字典到自定义类实例的转换。
5.1 object_hook — 字典到对象的转换
object_hook是loads/load函数的一个参数,接受一个可调用对象。在解析过程中,每当遇到JSON对象(即Python字典)时,都会先解析为dict,然后自动调用object_hook函数进行处理。这是将JSON数据转换回自定义类实例的最简方式。
import json
class Person:
def __init__(self, name, age, role):
self.name = name
self.age = age
self.role = role
def __repr__(self):
return f"Person(name={self.name!r}, age={self.age}, role={self.role!r})"
def person_hook(dct):
"""将带有type标记的字典转换为Person实例"""
if "type" in dct and dct["type"] == "Person":
return Person(
name=dct.get("name", ""),
age=dct.get("age", 0),
role=dct.get("role", "user")
)
return dct # 其他字典原样返回
json_str = '''
{
"type": "Person",
"name": "Charlie",
"age": 28,
"role": "developer"
}
'''
person = json.loads(json_str, object_hook=person_hook)
print(person)
# 输出:Person(name='Charlie', age=28, role='developer')
print(type(person))
# 输出:
5.2 object_pairs_hook — 保留键顺序
object_pairs_hook与object_hook类似,但传入的是有序的键值对列表而非字典。这对于需要保留JSON对象键顺序、处理重复键或创建OrderedDict等场景非常有用。注意object_pairs_hook的优先级高于object_hook。
import json
from collections import OrderedDict
json_str = '{"b": 1, "a": 2, "c": 3}'
# 使用object_pairs_hook创建OrderedDict
result = json.loads(json_str, object_pairs_hook=OrderedDict)
print(list(result.keys()))
# 输出:['b', 'a', 'c'] —— 保留了JSON中的原始顺序
# 处理重复键(JSON规范不推荐,但实际数据可能存在)
duplicate_json = '{"key": "first", "key": "second"}'
def collect_pairs(pairs):
"""收集重复键的所有值"""
result = OrderedDict()
for k, v in pairs:
if k in result:
if not isinstance(result[k], list):
result[k] = [result[k]]
result[k].append(v)
else:
result[k] = v
return result
parsed = json.loads(duplicate_json, object_pairs_hook=collect_pairs)
print(parsed)
# 输出:OrderedDict([('key', ['first', 'second'])])
5.3 JSONDecoder子类定制
更正式的自定义解码方案是继承JSONDecoder并覆盖相关方法。这种方式在框架集成和复杂解码逻辑中更常用。
import json
from datetime import datetime
class CustomDecoder(json.JSONDecoder):
"""支持ISO格式日期字符串自动转为datetime的自定义解码器"""
def __init__(self, *args, **kwargs):
super().__init__(
*args,
**kwargs,
object_hook=self._object_hook
)
@staticmethod
def _object_hook(dct):
"""递归处理字典中的值,将ISO日期字符串转为datetime"""
for key, value in dct.items():
if isinstance(value, str):
# 尝试解析ISO格式日期
try:
dct[key] = datetime.fromisoformat(value)
except (ValueError, TypeError):
pass
elif isinstance(value, dict):
dct[key] = CustomDecoder._object_hook(value)
return dct
# 使用自定义解码器
json_str = '''{
"title": "会议",
"start": "2026-05-05T14:30:00",
"end": "2026-05-05T16:00:00",
"location": {"name": "会议室A", "floor": 3}
}'''
data = json.loads(json_str, cls=CustomDecoder)
print(data["start"], type(data["start"]))
# 输出:2026-05-05 14:30:00
print(data["location"]["name"], type(data["location"]["name"]))
# 输出:会议室A (非日期字符串不会被误转)
5.4 parse_float / parse_int 进阶用法
除了object_hook系列参数,parse_float和parse_int参数允许在更低层级拦截数字的解析过程。这在需要精确控制数字类型(如避免浮点精度问题)时非常强大。
from decimal import Decimal, ROUND_HALF_UP
# 将所有JSON数字解析为Decimal,避免浮点精度问题
json_str = '{"total": 0.1, "tax": 0.2, "sum": 0.3}'
# 默认行为 —— 浮点数精度问题
default = json.loads(json_str)
print(default["total"] + default["tax"]) # 0.30000000000000004
# 使用Decimal解析
precise = json.loads(
json_str,
parse_float=Decimal,
parse_int=Decimal
)
print(precise["total"] + precise["tax"]) # 0.3
print(precise["total"] + precise["tax"] == precise["sum"]) # True
注意: object_hook的调用发生在字典构建完成之后,parse_float/parse_int的调用发生在JSON词法分析阶段。因此parse_float/parse_int对所有JSON数字生效,而object_hook仅对JSON对象(字典)生效。理解这一层次关系有助于选择正确的扩展点。
六、中文与特殊处理
在实际项目中,json模块最常遇到的三个特殊处理场景是:中文编码、日期时间序列化、Decimal数值精度。本节深入讲解这些场景的最佳实践方案。
6.1 中文处理 — ensure_ascii=False
默认情况下,json.dumps会将所有非ASCII字符(包括中文)转义为\\uXXXX形式的Unicode转义序列。这种输出的可读性极差,在调试、日志记录、数据交换等场景中都会带来困扰。设置ensure_ascii=False即可保留原始字符。
import json
data = {
"title": "Python json模块学习笔记",
"author": "学习者",
"tags": ["序列化", "编码解码", "数据处理"],
"description": "本章详细讲解json模块的核心用法"
}
# 默认行为
default_output = json.dumps(data, indent=2)
print(default_output)
# 输出(不可读):
# {
# "title": "\\u0050\\u0079\\u0074\\u0068\\u006f\\u006e ......"
# }
# 正确做法
readable_output = json.dumps(data, indent=2, ensure_ascii=False)
print(readable_output)
# 输出(完全可读):
# {
# "title": "Python json模块学习笔记",
# "author": "学习者",
# "tags": ["序列化", "编码解码", "数据处理"],
# "description": "本章详细讲解json模块的核心用法"
# }
写入文件时的编码配合: 使用ensure_ascii=False时,务必确保文件打开时指定了正确的编码,否则写入操作可能导致编码错误。
# 正确的文件写入方式
with open("notes.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
# 错误的写入方式 —— 可能引发编码错误或乱码
# with open("notes.json", "w") as f: # 默认使用locale编码
# json.dump(data, f, indent=2, ensure_ascii=False)
6.2 日期时间序列化
Python的datetime、date、time等类型不是JSON原生支持的类型,需要转换为字符串后再序列化。常见的策略包括ISO格式字符串、时间戳、自定义格式。选择合适的方案取决于使用场景。
import json
from datetime import datetime, date, time, timedelta
# 方案一:ISO格式(推荐用于人类可读场景)
def datetime_to_iso(obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
if isinstance(obj, time):
return obj.isoformat(timespec="seconds")
if isinstance(obj, timedelta):
return obj.total_seconds()
raise TypeError(f"Type {type(obj)} not serializable")
data = {
"event": "系统上线",
"timestamp": datetime.now(),
"launch_date": date(2026, 6, 1),
"duration": timedelta(hours=2, minutes=30)
}
result = json.dumps(data, default=datetime_to_iso, indent=2, ensure_ascii=False)
print(result)
# 输出:
# {
# "event": "系统上线",
# "timestamp": "2026-05-05T23:56:48.123456",
# "launch_date": "2026-06-01",
# "duration": 9000.0
# }
# 方案二:时间戳(推荐用于机器处理、排序场景)
def datetime_to_timestamp(obj):
if isinstance(obj, datetime):
return obj.timestamp()
if isinstance(obj, date):
return datetime(obj.year, obj.month, obj.day).timestamp()
raise TypeError(f"Type {type(obj)} not serializable")
result_ts = json.dumps(
{"event": "start", "at": datetime.now()},
default=datetime_to_timestamp
)
print(result_ts)
# 输出:{"event": "start", "at": 1746467808.123456}
6.3 Decimal编码与精度控制
Decimal类型用于高精度数值计算(如金融、科学计算),但JSON不原生支持。常见的处理方式有三种:转为浮点数(有精度损失)、转为字符串(保留精度但失去数值类型)、使用自定义对象表示。
import json
from decimal import Decimal, ROUND_HALF_UP
# 方式一:转为float(可能导致精度损失)
def decimal_to_float(obj):
if isinstance(obj, Decimal):
return float(obj)
raise TypeError
d = Decimal("19.99")
print(json.dumps({"price": d}, default=decimal_to_float))
# 输出:{"price": 19.99}
# 方式二:转为字符串(保留精度,但解码后是字符串)
def decimal_to_str(obj):
if isinstance(obj, Decimal):
return str(obj)
raise TypeError
print(json.dumps({"price": d}, default=decimal_to_str))
# 输出:{"price": "19.99"}
# 方式三:使用自定义标记对象(推荐,信息最完整)
def decimal_to_marked(obj):
if isinstance(obj, Decimal):
return {"__type__": "Decimal", "value": str(obj)}
raise TypeError
print(json.dumps({"price": d}, default=decimal_to_marked, indent=2))
# 输出:
# {
# "price": {
# "__type__": "Decimal",
# "value": "19.99"
# }
# }
# 配合自定义解码器还原
def decimal_decoder(dct):
if "__type__" in dct and dct["__type__"] == "Decimal":
return Decimal(dct["value"])
return dct
encoded = json.dumps({"price": d}, default=decimal_to_marked)
decoded = json.loads(encoded, object_hook=decimal_decoder)
print(decoded["price"], type(decoded["price"]))
# 输出:19.99
# 精度完全保留,可以进行精确计算
6.4 其他特殊类型处理
除上述常见场景外,实际项目中还可能遇到以下特殊类型的序列化需求:
import json
import numpy as np
from enum import Enum
from pathlib import Path
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
class UniversalEncoder(json.JSONEncoder):
"""通用编码器,覆盖更多特殊类型"""
def default(self, o):
# Enum类型
if isinstance(o, Enum):
return {"__enum__": type(o).__name__, "value": o.value}
# numpy数值类型
if isinstance(o, (np.integer,)):
return int(o)
if isinstance(o, (np.floating,)):
return float(o)
if isinstance(o, np.ndarray):
return o.tolist()
# Path路径
if isinstance(o, Path):
return str(o.as_posix())
# bytes
if isinstance(o, bytes):
return o.hex()
# 正则表达式
import re
if isinstance(o, re.Pattern):
return {"__regex__": o.pattern, "flags": o.flags}
return super().default(o)
# 测试
import re
data = {
"color": Color.RED,
"pattern": re.compile(r"\\d+\\.\\d+"),
"binary": b"\\x00\\x01\\x02"
}
print(json.dumps(data, cls=UniversalEncoder, indent=2))
# 输出:
# {
# "color": {"__enum__": "Color", "value": 1},
# "pattern": {"__regex__": "\\d+\\.\\d+", "flags": 256},
# "binary": "000102"
# }
七、核心总结
一、四大核心函数
json.dumps() —— Python对象转JSON字符串,序列化的核心入口
json.dump() —— Python对象直接序列化写入文件流
json.loads() —— JSON字符串解析为Python对象,反序列化核心入口
json.load() —— 从文件流读取并解析JSON数据
二、编码参数速记
sort_keys=True —— 键排序输出,保证一致性
indent=N —— 格式化缩进,提升可读性
separators=(',', ':') —— 紧凑输出,减少带宽
ensure_ascii=False —— 保留中文等非ASCII字符
skipkeys=True —— 跳过非标准键类型的容错模式
default=func —— 兜底序列化函数,处理不可序列化类型
三、自定义扩展机制
继承JSONEncoder + 覆盖default() —— 自定义序列化(推荐)
object_hook回调函数 —— 自定义反序列化字典到对象转换
object_pairs_hook —— 保留键顺序或处理重复键
parse_float/parse_int —— 低层级数字类型拦截
继承JSONDecoder —— 完整的自定义解码器方案
四、常见陷阱与最佳实践
写入文件务必指定encoding="utf-8",读取文件务必使用文本模式
Decimal序列化建议使用"标记对象"方案(如{"__type__":"Decimal","value":"..."})以无损保留精度
ensure_ascii=False与indent配合使用,可同时保证可读性和正确性
处理超大JSON文件时考虑ijson流式解析,避免内存溢出
网络传输场景建议设置separators=(',', ':')以最小化数据体积
JSON格式严格遵循RFC 7159,不支持单引号、注释、尾随逗号等Python风格写法
学习提示: json模块是Python标准库中"小而精"的典范。它的API设计清晰——四个函数构成核心,两个类支撑扩展。掌握json模块不仅是为了处理JSON数据,更能帮助理解"序列化协议"的设计思想:如何定义类型映射、如何提供扩展点、如何在严格与灵活之间取得平衡。这些思想同样适用于pickle、yaml、msgpack等其他序列化格式的学习。