21_JSON与序列化
理解 JSON 数据类型,使用标准库读写 JSON,并校验外部数据。
Python 基础专题 21:JSON 与序列化
学习目标:理解 JSON 数据类型,使用标准库读写 JSON,并校验从外部取得的数据。
核心知识
JSON 是跨语言交换数据的文本格式。它有对象、数组、字符串、数字、布尔值和 null。Python 常把 JSON 对象解析为 dict、数组解析为 list、null 解析为 None。转换后的类型只是解析结果,尚不代表字段符合业务要求。JSON 不支持注释、单引号字符串、末尾多余逗号,也不直接表示 set、datetime 或任意 Python 类实例。
| JSON | Python | 例子 |
|---|---|---|
| object | dict |
{"title":"学习"} |
| array | list |
[1,2] |
| true / false | True / False |
true |
| null | None |
null |
json.dumps(value) 把 Python 值编码成 JSON 字符串,json.loads(text) 从字符串解析值;文件对象使用 json.dump、json.load。ensure_ascii=False 让中文以可读形式写出;indent=2 便于人工检查。对外发送时仍应明确 UTF-8 编码,不能以显示方式推断网络字节。
import json
note = {"id": 1, "title": "学习 Python", "done": False}
text = json.dumps(note, ensure_ascii=False, indent=2)
decoded = json.loads(text)
assert decoded["title"] == "学习 Python"
assert decoded["done"] is False
从文件读取并校验
外部 JSON 可能无效,json.loads 会抛 JSONDecodeError;文件可能不存在或无法读取,也会抛相应的文件异常。解析成功后,根值可能是列表、数字或 null,不能直接假设是字典。字段也可能缺失、类型错误或只含空白。先检查结构,再创建业务对象。
import json
from pathlib import Path
def load_title(path: Path) -> str:
raw = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(raw, dict):
raise ValueError("根节点必须是对象")
title = raw.get("title")
if not isinstance(title, str) or not title.strip():
raise ValueError("title 必须是非空字符串")
return title.strip()
这个函数把 JSON 格式错误留给调用方处理,把业务结构错误转成 ValueError。若用于面向用户的程序,入口可以分别处理 FileNotFoundError、JSONDecodeError 与 ValueError,给出不同提示。写回文件时,path.write_text(json.dumps(...), encoding="utf-8") 会覆盖原内容;重要数据还要考虑原子替换、备份和并发修改。
逐步理解
把“解码成功”与“数据可用”分成两关:第一关检查文本是否符合 JSON 语法,第二关检查键、类型和取值范围。JSON 数字解析成 Python int 或 float,金额若需要精确十进制,应明确业务表示方式,例如以分为单位的整数。不要使用 pickle 代替 JSON 处理不可信输入;它面向 Python 对象,并可能在反序列化时执行代码。
动手练习
练习:创建一个含 title 的 JSON 文件,用 load_title 读取;再分别测试无效 JSON、缺少 title、title 为数字、title 仅含空格。答案要点:第一种抛 JSONDecodeError,后三种抛 ValueError;合法文本返回去掉首尾空格的标题。