Python 如何正确读写 JSON?json.dump、dumps、load、loads 有什么区别?
简化版
json.dumps() 把 Python 对象转成 JSON 字符串,json.dump() 把对象写入文件;json.loads() 从 JSON 字符串解析对象,json.load() 从文件读取并解析。读写中文时常用 ensure_ascii=False,文件编码建议显式指定 encoding="utf-8"。
详细版
四个 API 的区别看名字:带 s 的处理 string,不带 s 的处理 file object。
import json
text = json.dumps({"name": "张三"}, ensure_ascii=False)
data = json.loads(text)
with open("data.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
with open("data.json", "r", encoding="utf-8") as f:
data = json.load(f)
JSON 只支持有限类型:对象、数组、字符串、数字、布尔值和 null。datetime、Decimal、自定义对象默认不能直接序列化,需要转换或自定义 encoder。
完整版教学
一、为什么 JSON 是面试高频
JSON 是 Web API、配置文件、日志交换、前后端通信中最常见的数据格式之一。Python 项目里经常需要把 dict/list 写成 JSON,或从接口响应中解析 JSON。
Python dict/list <-> JSON text <-> 文件/API/消息
它看起来简单,但中文编码、日期、Decimal、浮点精度、大文件读取都容易踩坑。
二、dump 和 dumps 的区别
dumps 的 s 表示 string,返回 JSON 字符串;dump 写入文件对象,不返回完整字符串。
obj = {"id": 1}
json_text = json.dumps(obj)
with open("a.json", "w", encoding="utf-8") as f:
json.dump(obj, f)
| API | 输入 | 输出 |
|---|---|---|
dumps | Python 对象 | JSON 字符串 |
dump | Python 对象 + 文件 | 写文件 |
loads | JSON 字符串 | Python 对象 |
load | 文件 | Python 对象 |
记住“s = string”,基本不会混。
三、中文为什么会变成转义
默认 ensure_ascii=True,非 ASCII 字符会变成 \uXXXX 转义。这样兼容性好,但人读文件不友好。写中文配置或日志时常用 ensure_ascii=False。
json.dumps({"name": "张三"})
# {"name": "\u5f20\u4e09"}
json.dumps({"name": "张三"}, ensure_ascii=False)
# {"name": "张三"}
同时文件要显式指定 UTF-8,否则在不同系统默认编码下可能出错。
中文 JSON 的稳妥组合是
encoding="utf-8"+ensure_ascii=False。
四、哪些对象不能直接序列化
JSON 标准没有 datetime、Decimal、set、自定义对象。Python 的 json 模块遇到这些类型会抛 TypeError。
from datetime import datetime
json.dumps({"time": datetime.now()}) # TypeError
常见做法是先转换:
json.dumps({"time": datetime.now().isoformat()})
或提供 default 函数,把无法序列化的对象转成字符串、数字或 dict。
五、浮点数和 Decimal 的边界
JSON 数字没有区分 Python 的 float 和 Decimal。如果金融金额用 float 序列化再解析,可能带来精度问题。金额通常用字符串或整数分保存。
19.99 元 -> 1999 分
如果必须保留 Decimal,可以在序列化时转字符串,读取后再转回 Decimal。API 契约要明确字段类型,否则调用方会误用。
六、大 JSON 文件要注意什么
json.load(f) 会把整个 JSON 解析进内存。100MB 文件可能占用远超 100MB 的内存,因为 Python 对象有额外开销。如果是 JSON Lines,每行一个 JSON,可以逐行处理。
with open("data.jsonl", encoding="utf-8") as f:
for line in f:
obj = json.loads(line)
handle(obj)
大数组 JSON 需要流式解析库,标准库不太适合超大 JSON 全量解析。
七、常见误区与追问
- 误区:dump 和 dumps 一样。 dumps 返回字符串,dump 写文件。
- 误区:JSON 能保存任意 Python 对象。 datetime、set、自定义对象默认不能直接序列化。
- 误区:中文转义是乱码。
\uXXXX是合法 JSON 转义,不是乱码,只是可读性差。 - 追问:如何漂亮格式化? 用
indent=2,必要时配合sort_keys=True。 - 追问:金额字段怎么保存? 用整数分或字符串,避免 float 精度问题。
- 追问:大文件怎么读? JSON Lines 可逐行读;大数组要考虑流式解析库。
- 追问:接口返回 JSON 解析失败怎么办? 捕获
json.JSONDecodeError并记录原始响应片段。
八、加强记忆
JSON 四个函数记住“有 s 处理字符串,无 s 处理文件”。中文用 UTF-8 和 ensure_ascii=False,特殊对象先转换,大文件别一口吞,金额别随手 float。面试里把 API 区别、编码、类型边界和大文件处理一起讲,就够完整。