JSON 和 Python 用的是同样的结构
JSON 是 API、配置文件和 Web 数据交换的默认格式。对 Python 程序员来说幸运的是,JSON 对象直接对应 Python 的字典,JSON 数组对应列表,JSON 字符串对应 str。正因为结构如此接近,在 Python 中读写 JSON 只需要两行代码。
标准库模块 json 负责这两个方向的转换。
解析 JSON 字符串
json.loads(text)(意思是“load string”)接收一个 JSON 格式的字符串,返回它所表示的 Python 对象:
结果就是一个普通的字典。你可以像访问任何字典一样访问它的键,没有特殊的 JSON 包装,结果上也没有 .parse() 方法。
如果字符串不是有效的 JSON,json.loads 会抛出 json.JSONDecodeError。错误信息中包含出问题的行号和列号,通常足以让你找出漏掉的逗号或没有转义的引号。
把 JSON 写成字符串
json.dumps(data)(意思是“dump string”)做的事情正好相反:接收一个 Python 对象,返回一个 JSON 字符串:
Python 的 True、False 和 None 会被自动转换为 JSON 的 true、false 和 null。数字和字符串保持不变。列表变成数组,字典变成对象。
格式化输出
json.dumps 默认的输出很紧凑,适合网络传输,但人读起来很费劲。传入 indent=2 可以得到更易读的结果:
另外几个值得了解的 dumps 选项:
sort_keys=True:按字母顺序对对象的键排序。适合需要确定性输出的场景(配置文件、测试数据、对比差异)。ensure_ascii=False:把非 ASCII 字符(é、ü、中)原样写出,而不是写成\u转义序列。对于 UTF-8 文件,这通常是正确的选择。separators=(",", ":"):输出尽可能紧凑。再配合ensure_ascii=False,就能生成最紧凑的 UTF-8 JSON。
从文件读取 JSON
json.load(file)(不带 s)直接从文件对象读取。常见的写法是和 with open 搭配使用:
import json
with open("config.json") as f:
config = json.load(f)
print(config["version"])
不需要先把整个文件读成字符串;json.load 会直接从文件对象中读取。
把 JSON 写入文件
json.dump(data, file) 是对应的写文件方法:
import json
data = {"created": "2026-01-01", "items": [1, 2, 3]}
with open("state.json", "w") as f:
json.dump(data, f, indent=2)
indent 选项在这里同样适用。打开生成的文件,你会看到一个格式整齐的 JSON 文档。
读取 JSON 格式的 API 响应
大多数 HTTP 库给你的是字节或文本,你需要调用 json.loads 把它变成可用的数据:
import json
import urllib.request
with urllib.request.urlopen("https://api.example.com/users/1") as response:
text = response.read().decode("utf-8")
user = json.loads(text)
print(user["name"])
requests 库(另有专门的页面介绍)可以省掉一步:响应对象上有一个 .json() 方法,会替你调用 json.loads。
JSON 能表示什么,不能表示什么
JSON 的类型系统比 Python 的窄。两个方向的对应关系如下:
| Python | JSON |
|---|---|
dict | 对象 |
list, tuple | 数组 |
str | 字符串 |
int, float | 数字 |
True | true |
False | false |
None | null |
元组转换后再转回来会变成列表,元组的特性会丢失。集合、自定义类和 datetime 对象默认无法序列化,你会得到 TypeError: Object of type X is not JSON serializable。
处理 datetime 和自定义对象
有两种常见做法。
先转换成可以序列化的结构。 在你自己的代码中完成转换,然后序列化一个普通字典:
传入一个 default= 函数。 遇到不知道如何序列化的值时,json.dumps 会调用它:
读取数据时,你需要自己把这些 ISO 字符串转换回 datetime,因为 JSON 不会记住它们原来是什么类型。
字典经过 JSON 的一次往返
快速验证一下字典能否完整保留:
两者的值相等,但它们是不同的对象。这往往正是你想要的:json.dumps 加 json.loads 是对任何兼容 JSON 的结构做深拷贝的一种简便方法。
一个贴近实际的例子
一个小脚本:加载 JSON 配置,更新一个字段,然后保存回去:
import json
from pathlib import Path
config_path = Path("settings.json")
# Load (with a default if the file doesn't exist).
if config_path.exists():
config = json.loads(config_path.read_text())
else:
config = {"theme": "dark", "last_opened": None}
# Update.
config["last_opened"] = "2026-01-15"
# Save, pretty-printed.
config_path.write_text(json.dumps(config, indent=2, ensure_ascii=False))
十几行代码就完成了一个完整的 JSON“读取、修改、写入”流程。
几个好习惯
- 处理文件时始终用
with open(...)。 JSON 不过是文本,所有文件操作的规则都适用。 - 给人看的文件优先用
indent=2,比如配置文件、测试数据、导出的数据。对于注重紧凑的网络传输,可以省略。 - 输出 UTF-8 时设置
ensure_ascii=False,让带重音符号的名字或中文等非拉丁字符保持可读。 - 解析不是你生成的数据时,用
try/except json.JSONDecodeError做校验。
下一篇
JSON 处理的是键值数据。同一章的下一个工具是 CSV:Python 对表格格式的内置支持,你遇到的大多数电子表格导出文件用的都是这种格式。
常见问题
Python 中怎么解析 JSON?
JSON 字符串用 json.loads(text),文件对象用 json.load(file)。两者都会返回一个 Python 字典(或列表,取决于 JSON 的内容)。例如:data = json.loads('{"name": "Rosa"}'),此时 data['name'] 是 'Rosa'。
Python 中怎么把字典转换成 JSON?
json.dumps(my_dict) 返回一个 JSON 字符串。json.dump(my_dict, file) 直接写入一个文件对象。传入 indent=2 可以得到格式化的输出:json.dumps(data, indent=2)。
json.loads 和 json.load 有什么区别?
loads(带 s)接收一个字符串。load(不带 s)接收一个文件对象。dumps 和 dump 也是一样。s 代表 string(字符串),这样记最容易分清。
JSON 中怎么处理日期和自定义对象?
JSON 没有日期类型,所以要把日期作为 ISO-8601 字符串传递,读取时再手动解析回来。对于自定义类,可以给 json.dumps 提供一个 default= 函数,让它返回可以转成 JSON 的表示形式;也可以在序列化之前自己把对象转换成 dict。