“在Python的世界里,JSON处理看似简单,但选对库能让你的应用性能提升10倍。
引言:为什么需要关心JSON库的选择
JSON(JavaScript Object Notation)已经成为现代软件开发中数据交换的事实标准。无论是Web API、配置文件、日志记录,还是数据存储,JSON无处不在。
Python内置了json模块,对于大多数日常场景来说完全够用。但当你的应用面临以下挑战时,JSON库的选择就变得至关重要:
- 高并发API服务:每秒需要处理数千次JSON序列化/反序列化
在这种情况下,选择不同的JSON库可能带来4倍到11倍的性能差异。这就是为什么我们需要深入了解json、ujson和orjson这三个主流选择。
第一部分:三大JSON库概览
json - Python标准库
身份:Python内置模块,无需安装
特点:
- ✅ API简单:
json.dumps()和json.loads()人人都会用 - ❌ 功能有限:不支持datetime、UUID等特殊类型
历史:自Python 2.6起成为标准库,是最早被广泛使用的JSON库。
适用场景:小型项目、配置文件处理、性能要求不高的场景。
ujson - UltraJSON
身份:用C语言编写的高性能JSON库
特点:
历史:2011年由字节跳动(当时的今日头条)开源,曾是高性能JSON处理的首选。
安装:
pip install ujson
适用场景:需要比标准库更快,但又不想改变代码结构的项目。
orjson - Rust-powered JSON库
身份:用Rust编写的现代高性能JSON库
特点:
- ✅ 特殊类型支持:原生支持datetime、UUID、dataclass、numpy等
- ✅ 严格规范:严格遵守JSON RFC 8259标准
- ❌ 返回bytes:
dumps()返回bytes而非str - ❌ 需要安装:需要编译Rust扩展(但pip会提供预编译wheel)
历史:2019年由ijl开发,迅速成为Python生态中最快的JSON库。
安装:
pip install orjson
适用场景:高性能API、大数据处理、需要处理复杂Python类型的场景。
第二部分:性能对比分析
基准测试环境
序列化(dumps)性能测试
import time
import json
import ujson
import orjson
# 准备测试数据
data = {
"name": "性能测试",
"values": [i for i in range(10000)],
"nested": {"key": "value" * 100}
}
defbenchmark_dumps():
# 标准json
start = time.time()
for _ in range(300000):
json.dumps(data)
std_time = time.time() - start
# ujson
start = time.time()
for _ in range(300000):
ujson.dumps(data)
ujson_time = time.time() - start
# orjson
start = time.time()
for _ in range(300000):
orjson.dumps(data)
orjson_time = time.time() - start
print(f"标准json dumps时间: {std_time:.4f}s")
print(f"ujson dumps时间: {ujson_time:.4f}s (快{std_time/ujson_time:.1f}倍)")
print(f"orjson dumps时间: {orjson_time:.4f}s (快{std_time/orjson_time:.1f}倍)")
benchmark_dumps()
典型结果:
标准json dumps时间: 45.2341s
ujson dumps时间: 18.7654s (快2.4倍)
orjson dumps时间: 4.1234s (快11.0倍)
反序列化(loads)性能测试
defbenchmark_loads():
json_str = json.dumps(data)
# 标准json
start = time.time()
for _ in range(300000):
json.loads(json_str)
std_time = time.time() - start
# ujson
start = time.time()
for _ in range(300000):
ujson.loads(json_str)
ujson_time = time.time() - start
# orjson
start = time.time()
for _ in range(300000):
orjson.loads(json_str)
orjson_time = time.time() - start
print(f"\n标准json loads时间: {std_time:.4f}s")
print(f"ujson loads时间: {ujson_time:.4f}s (快{std_time/ujson_time:.1f}倍)")
print(f"orjson loads时间: {orjson_time:.4f}s (快{std_time/orjson_time:.1f}倍)")
benchmark_loads()
典型结果:
标准json loads时间: 38.5678s
ujson loads时间: 15.2341s (快2.5倍)
orjson loads时间: 6.7890s (快5.7倍)
不同数据规模下的表现
性能总结
- orjson在几乎所有场景下都是最快的,尤其是大数据集
第三部分:功能特性对比
数据类型支持对比
| | | |
|---|
| dict/list/str/int/float/bool | | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
特殊类型处理示例
标准json需要自定义编码器:
import json
from datetime import datetime
from uuid import UUID
classCustomEncoder(json.JSONEncoder):
defdefault(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, UUID):
return str(obj)
return super().default(obj)
data = {
"timestamp": datetime.now(),
"id": UUID('12345678-1234-5678-1234-567812345678')
}
# 必须指定encoder
json_str = json.dumps(data, cls=CustomEncoder)
orjson原生支持:
import orjson
from datetime import datetime
from uuid import UUID
data = {
"timestamp": datetime.now(),
"id": UUID('12345678-1234-5678-1234-567812345678')
}
# 直接序列化,无需自定义
json_bytes = orjson.dumps(data)
输出格式控制
orjson提供丰富的选项:
import orjson
data = {"name": "测试", "value": 42, "中文": "你好"}
# 美化输出(缩进2)
pretty = orjson.dumps(data, option=orjson.OPT_INDENT_2)
print(pretty.decode())
# 输出:
# {
# "name": "测试",
# "value": 42,
# "中文": "你好"
# }
# 排序键
sorted_keys = orjson.dumps(data, option=orjson.OPT_SORT_KEYS)
print(sorted_keys.decode())
# 输出: {"value": 42, "name": "测试", "中文": "你好"}
# 非ASCII字符不转义
non_ascii = orjson.dumps(data, option=orjson.OPT_NON_STR_KEYS)
print(non_ascii.decode())
# 输出: {"name": "测试", "value": 42, "中文": "你好"}
# 组合多个选项
combined = orjson.dumps(
data,
option=orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS
)
标准json的等效实现:
import json
# 美化输出
pretty = json.dumps(data, indent=2, ensure_ascii=False)
# 排序键
sorted_keys = json.dumps(data, sort_keys=True, ensure_ascii=False)
JSON规范遵守程度
重要区别:
import json
import orjson
import math
# 标准json允许非标准值
data = {"value": float('nan')}
print(json.dumps(data)) # 输出: {"value": NaN}
# orjson严格遵循标准,拒绝非标准值
try:
orjson.dumps({"value": float('nan')})
except TypeError as e:
print(f"orjson错误: {e}") # 输出: Type is not JSON serializable
第四部分:使用指南与代码示例
json模块 - 基础使用
1. 基本序列化与反序列化
import json
# 序列化:Python对象 -> JSON字符串
data = {
"name": "张三",
"age": 30,
"skills": ["Python", "JavaScript"],
"active": True
}
json_str = json.dumps(data)
print(json_str)
# 输出: {"name": "\u5f20\u4e09", "age": 30, ...}
# 中文不转义
json_str_cn = json.dumps(data, ensure_ascii=False)
print(json_str_cn)
# 输出: {"name": "张三", "age": 30, ...}
# 反序列化:JSON字符串 -> Python对象
loaded_data = json.loads(json_str)
print(loaded_data["name"]) # 输出: 张三
2. 文件操作
import json
# 写入JSON文件
data = {"name": "李四", "score": 95}
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# 读取JSON文件
with open('data.json', 'r', encoding='utf-8') as f:
loaded_data = json.load(f)
print(loaded_data) # 输出: {'name': '李四', 'score': 95}
3. 自定义编码器
import json
from datetime import datetime
classDateTimeEncoder(json.JSONEncoder):
defdefault(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)
data = {
"created_at": datetime.now(),
"message": "测试消息"
}
# 使用自定义编码器
json_str = json.dumps(data, cls=DateTimeEncoder)
print(json_str)
4. 自定义解码器
import json
from datetime import datetime
defdatetime_decoder(dct):
for key, value in dct.items():
if isinstance(value, str):
try:
dct[key] = datetime.fromisoformat(value)
except ValueError:
pass
return dct
json_str = '{"created_at": "2024-01-01T12:00:00", "message": "测试"}'
data = json.loads(json_str, object_hook=datetime_decoder)
print(data["created_at"]) # 输出: datetime对象
ujson - 快速上手
1. 与json模块几乎相同的API
import ujson
# 序列化
data = {"name": "王五", "age": 25, "city": "北京"}
json_str = ujson.dumps(data, ensure_ascii=False)
print(json_str) # 输出: {"name":"王五","age":25,"city":"北京"}
# 反序列化
loaded_data = ujson.loads(json_str)
print(loaded_data["name"]) # 输出: 王五
# 文件操作
with open('data.json', 'w') as f:
ujson.dump(data, f)
with open('data.json', 'r') as f:
loaded_data = ujson.load(f)
2. ujson的特有选项
import ujson
data = {"name": "测试", "value": 42}
# 美化输出
pretty = ujson.dumps(data, indent=2)
print(pretty)
# 转义正斜杠
escaped = ujson.dumps(data, escape_forward_slashes=True)
# 编码为bytes
bytes_output = ujson.dumps(data, encode_utf8=True)
3. 注意事项
import ujson
# ujson在某些边缘情况下行为不同
# 1. 超大整数可能被转为浮点数
large_int = {"value": 99999999999999999999999}
print(ujson.loads(ujson.dumps(large_int)))
# 2. 不支持NaN/Infinity的字符串表示
# 3. 键必须是字符串
orjson - 高级特性
1. 基础使用
import orjson
from datetime import datetime
from uuid import uuid4
# 注意:orjson.dumps()返回bytes,需要decode()
data = {
"id": str(uuid4()),
"timestamp": datetime.now(),
"message": "Hello World"
}
# 序列化
json_bytes = orjson.dumps(data)
json_str = json_bytes.decode('utf-8')
print(json_str)
# 反序列化
loaded_data = orjson.loads(json_bytes)
print(loaded_data["message"])
2. 处理dataclass
import orjson
from dataclasses import dataclass
@dataclass
classUser:
id: int
name: str
email: str
is_active: bool
user = User(id=1, name="赵六", email="zhaoliu@example.com", is_active=True)
# orjson原生支持dataclass
json_bytes = orjson.dumps(user)
print(json_bytes.decode())
# 输出: {"id":1,"name":"赵六","email":"zhaoliu@example.com","is_active":true}
# 嵌套dataclass
@dataclass
classPost:
title: str
author: User
content: str
post = Post(
title="orjson使用指南",
author=user,
content="这是一篇关于orjson的文章"
)
json_bytes = orjson.dumps(post)
print(json_bytes.decode())
3. 处理numpy数组
import orjson
import numpy as np
data = {
"matrix": np.array([[1, 2], [3, 4]]),
"vector": np.array([1.0, 2.0, 3.0]),
"scalar": np.float64(3.14)
}
# orjson原生支持numpy
json_bytes = orjson.dumps(data)
print(json_bytes.decode())
# 输出: {"matrix":[[1,2],[3,4]],"vector":[1.0,2.0,3.0],"scalar":3.14}
4. 处理bytes类型
import orjson
data = {
"binary": b"hello world",
"name": "测试"
}
# bytes会被base64编码
json_bytes = orjson.dumps(data)
print(json_bytes.decode())
# 输出: {"binary":"aGVsbG8gd29ybGQ=","name":"测试"}
5. 常用选项组合
import orjson
data = {
"z_key": "last",
"a_key": "first",
"中文键": "中文值",
"nested": {"data": [1, 2, 3]}
}
# 选项1:美化输出 + 非ASCII不转义
result1 = orjson.dumps(
data,
option=orjson.OPT_INDENT_2 | orjson.OPT_NON_STR_KEYS
)
print(result1.decode())
# 选项2:排序键 + 非ASCII不转义
result2 = orjson.dumps(
data,
option=orjson.OPT_SORT_KEYS | orjson.OPT_NON_STR_KEYS
)
print(result2.decode())
# 选项3:序列化numpy(需要OPT_NAIVE_UTC)
import numpy as np
from datetime import datetime
data_with_datetime = {
"timestamp": datetime.now(),
"array": np.array([1, 2, 3])
}
result3 = orjson.dumps(
data_with_datetime,
option=orjson.OPT_NAIVE_UTC
)
6. 反序列化的特殊处理
import orjson
# orjson.loads()可以接受bytes或str
json_bytes = b'{"name": "测试", "value": 42}'
json_str = '{"name": "测试", "value": 42}'
# 两种方式都可以
data1 = orjson.loads(json_bytes)
data2 = orjson.loads(json_str)
print(data1 == data2) # 输出: True
第五部分:选型建议与最佳实践
不同场景下的库选择
场景1:小型脚本/配置文件处理
推荐:json(标准库)
理由:
# config.py
import json
with open('config.json', 'r') as f:
config = json.load(f)
场景2:Web API服务(中等流量)
推荐:ujson
理由:
from fastapi import FastAPI
import ujson
app = FastAPI()
@app.get("/api/data")
asyncdefget_data():
data = {"status": "success", "items": [1, 2, 3]}
return ujson.loads(ujson.dumps(data))
场景3:高性能API/大数据处理
推荐:orjson
理由:
from fastapi import FastAPI
import orjson
app = FastAPI()
@app.get("/api/data")
asyncdefget_data():
data = {"status": "success", "items": [1, 2, 3]}
return orjson.loads(orjson.dumps(data))
# 自定义响应类以返回bytes
from fastapi.responses import Response
@app.get("/api/fast")
asyncdeffast_endpoint():
data = {"status": "success"}
return Response(
content=orjson.dumps(data),
media_type="application/json"
)
场景4:科学计算/数据分析
推荐:orjson
理由:
import orjson
import numpy as np
# 处理包含numpy的数据
data = {
"features": np.random.rand(1000, 10),
"labels": np.random.randint(0, 2, 1000)
}
json_bytes = orjson.dumps(data)
场景5:微服务间通信
推荐:orjson
理由:
import orjson
import requests
# 发送请求
data = {"user_id": 123, "action": "login"}
response = requests.post(
"http://service-a/api/login",
data=orjson.dumps(data),
headers={"Content-Type": "application/json"}
)
# 接收响应
result = orjson.loads(response.content)
性能优化技巧
技巧1:避免重复序列化
# ❌ 不好的做法
for item in items:
json_str = json.dumps(item) # 每次都序列化
send_to_network(json_str)
# ✅ 好的做法
json_str = json.dumps(items) # 一次性序列化
send_to_network(json_str)
技巧2:使用bytes避免额外解码
import orjson
# ❌ 不必要的解码
data = {"name": "测试"}
json_str = orjson.dumps(data).decode('utf-8') # bytes -> str
response.write(json_str.encode('utf-8')) # str -> bytes
# ✅ 直接使用bytes
data = {"name": "测试"}
json_bytes = orjson.dumps(data) # 直接返回bytes
response.write(json_bytes)
技巧3:批量处理大文件
import orjson
import json
# ❌ 一次性加载整个文件
with open('large.json', 'r') as f:
data = json.load(f) # 可能占用大量内存
# ✅ 流式处理(JSON Lines格式)
with open('large.jsonl', 'r') as f:
for line in f:
data = orjson.loads(line)
process(data)
技巧4:利用orjson的选项优化输出
import orjson
# 需要人类可读时使用美化输出
data = get_debug_info()
print(orjson.dumps(data, option=orjson.OPT_INDENT_2).decode())
# 生产环境使用紧凑输出节省带宽
data = get_api_response()
return orjson.dumps(data)
迁移注意事项
从json迁移到orjson
1. 返回值类型变化:
import json
import orjson
# json返回str
json_str = json.dumps({"name": "test"})
print(type(json_str)) # <class 'str'>
# orjson返回bytes
json_bytes = orjson.dumps({"name": "test"})
print(type(json_bytes)) # <class 'bytes'>
# 需要解码
json_str = json_bytes.decode('utf-8')
2. 移除自定义编码器:
import json
import orjson
from datetime import datetime
# json需要自定义编码器
classDateTimeEncoder(json.JSONEncoder):
defdefault(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
json_str = json.dumps({"time": datetime.now()}, cls=DateTimeEncoder)
# orjson原生支持,无需 to remove custom encoder
json_bytes = orjson.dumps({"time": datetime.now()})
3. NaN/Infinity处理:
import json
import orjson
import math
# json允许非标准值
data = {"nan": float('nan'), "inf": float('inf')}
json_str = json.dumps(data) # 正常工作
# orjson拒绝非标准值,需要预处理
data = {"nan": float('nan'), "inf": float('inf')}
# 方案1:替换为None
import numpy as np
data_clean = {
k: (Noneif (isinstance(v, float) and (math.isnan(v) or math.isinf(v))) else v)
for k, v in data.items()
}
json_bytes = orjson.dumps(data_clean)
# 方案2:使用字符串表示
data_clean = {
k: (str(v) if (isinstance(v, float) and (math.isnan(v) or math.isinf(v))) else v)
for k, v in data.items()
}
json_bytes = orjson.dumps(data_clean)
常见陷阱与解决方案
陷阱1:orjson的datetime时区处理
import orjson
from datetime import datetime
import pytz
# 带时区的datetime
dt_with_tz = datetime.now(pytz.UTC)
json_bytes = orjson.dumps({"time": dt_with_tz})
print(json_bytes.decode())
# 输出: {"time":"2024-01-01T12:00:00+00:00"}
# 不带时区的datetime(naive)
dt_naive = datetime.now()
json_bytes = orjson.dumps({"time": dt_naive})
print(json_bytes.decode())
# 输出: {"time":"2024-01-01T12:00:00"}
# 如果需要统一添加Z后缀
json_bytes = orjson.dumps(
{"time": dt_naive},
option=orjson.OPT_NAIVE_UTC
)
print(json_bytes.decode())
# 输出: {"time":"2024-01-01T12:00:00Z"}
陷阱2:ujson的精度问题
import ujson
# 超大整数可能丢失精度
large_int = 99999999999999999999999999
data = {"value": large_int}
json_str = ujson.dumps(data)
loaded = ujson.loads(json_str)
print(loaded["value"] == large_int) # 可能是False!
陷阱3:循环引用
import json
import orjson
# 创建循环引用
data = {"name": "test"}
data["self"] = data # 循环引用!
# json会抛出异常
try:
json.dumps(data)
except RecursionError as e:
print(f"json错误: {e}")
# orjson也会抛出异常
try:
orjson.dumps(data)
except TypeError as e:
print(f"orjson错误: {e}")
# 解决方案:避免循环引用或使用自定义序列化
结语:总结与展望
三个库的适用场景总结
选择决策树
需要处理JSON吗?
├── 是小型脚本/配置文件?
│ └── 使用 json(标准库)
│
├── 需要高性能但想保持API兼容?
│ └── 使用 ujson
│
├── 需要极致性能?
│ └── 使用 orjson
│
├── 需要处理datetime/UUID/numpy?
│ └── 使用 orjson
│
└── 不确定?
└── 从 json 开始,性能不够时再迁移
未来发展趋势
- orjson持续领先:随着Rust生态的成熟,orjson的性能优势可能会进一步扩大
- Python标准库改进:Python 3.x版本可能会引入C扩展来提升json模块性能
- simdjson崛起:基于SIMD指令集的解析库在大文件处理上表现优异
- AI时代的JSON:随着AI应用的发展,JSON处理性能将变得更加重要
个人建议
作为开发者,我推荐以下策略:
- 新项目:直接使用
orjson,它提供了最佳的性能和功能平衡 - 现有项目:如果性能不是瓶颈,保持使用
json;如果需要优化,优先迁移到orjson - 特殊场景:如果需要处理GB级别的JSON文件,考虑
simdjson - 团队协作:在项目中统一使用一个JSON库,避免混用导致的行为不一致
记住:过早优化是万恶之源。先用标准库,遇到性能瓶颈时再考虑迁移。但如果你已经知道项目会有大量JSON处理,直接使用orjson是明智的选择。
附录:快速参考表
API对比
| | | |
|---|
| json.dumps(obj) | ujson.dumps(obj) | orjson.dumps(obj) |
| json.loads(str) | ujson.loads(str) | orjson.loads(bytes/str) |
| json.dump(obj, f) | ujson.dump(obj, f) | |
| json.load(f) | ujson.load(f) | |
| indent=2 | indent=2 | option=OPT_INDENT_2 |
| sort_keys=True | | option=OPT_SORT_KEYS |
| ensure_ascii=False | ensure_ascii=False | |
安装命令
# json - 无需安装,Python内置
# ujson
pip install ujson
# orjson
pip install orjson
# 验证安装
python -c "import json; print('json OK')"
python -c "import ujson; print('ujson OK')"
python -c "import orjson; print('orjson OK')"
性能基准速查
在典型Web API场景下(100KB JSON数据):
本文基于Python 3.11和2024年最新版本的库编写。JSON库的性能会随版本更新而变化,建议在实际项目中自行进行基准测试。
如果你觉得这篇文章有帮助,欢迎分享给更多的开发者朋友!