前言
大家好,我是 倔强青铜三 。欢迎关注我,微信公众号:倔强青铜三。欢迎点赞、收藏、关注,一键三连!
欢迎来到 苦练Python第60天!
今天,咱们把" 数据交换界的普通话 "------JSON,彻底讲透!
用 Python 内置的 json
模块,10 分钟搞定 序列化、反序列化、自定义类型、命令行校验 全套技能。
学完它,你将彻底告别手写字符串拼接、正则解析等上古操作,优雅地完成 前后端通信、配置文件读写、单元测试断言、爬虫数据落地 等高频场景。
🎯 今日收获预览
- 基础四件套:
dump / dumps / load / loads
一次讲清 - 进阶四件套:自定义编码器、解码钩子、紧凑/美化输出、命令行神器
- 八大参数拆解:每个细节都配上代码
- 实战三板斧:配置文件热加载、API 数据校验、单元测试比对
- 常见坑位:NaN、Infinity、重复键、编码陷阱一次排雷
🧊 热身:为什么不用手撸?
灵魂三问:
- 你要不要处理转义
\"
、\u1234
? - 你要不要兼容
datetime
、Decimal
、UUID
? - 你要不要让 diff 工具一眼看懂?
json
模块全帮你 封装好了 ,还自带 UTF-8 自动识别。
一句话: 不要重复造轮子,除非你想加班。
🗡️ 第一式:基础四件套
1. json.dumps() vs json.dump()
函数 | 输出目标 | 典型用法 |
---|---|---|
json.dumps(obj) |
返回 str | 打日志、发网络 |
json.dump(obj, fp) |
写入 文件对象 | 写文件、写 socket |
一句话记忆: 带 s 的 return string,不带 s 的写文件。
python
import json, io
data = {'name': '青铜三', 'age': 18}
# 写内存
s = json.dumps(data, ensure_ascii=False)
print(s) # {"name": "青铜三", "age": 18}
# 写文件
with open('user.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
2. json.loads() vs json.load()
函数 | 输入源 | 典型用法 |
---|---|---|
json.loads(s) |
str/bytes/bytearray | 解析 HTTP 响应 |
json.load(fp) |
文件对象 | 读配置文件 |
一句话记忆: 带 s 的吃字符串,不带 s 的吃文件。
python
# 从字符串解析
user = json.loads('{"name": "青铜三", "age": 18}')
print(user) # {'name': '青铜三', 'age': 18}
# 从文件解析
with open('user.json', encoding='utf-8') as f:
user = json.load(f)
🧰 第二式:八大参数拆解
json.dumps() / json.dump() 共用参数
参数 | 作用 | 示例 |
---|---|---|
ensure_ascii=True |
非 ASCII 转义 \uXXXX |
设为 False 保留中文 |
indent=None |
美化缩进 | indent=4 或 "\t" |
sort_keys=False |
按键排序 | 回归测试必备 |
separators=None |
分隔符 | (',', ':') 最紧凑 |
default=None |
序列化不了时回调 | 自定义 datetime |
skipkeys=False |
非 str 键跳过 | True 时跳过而非抛错 |
allow_nan=True |
支持 NaN/Infinity | False 时严格模式抛错 |
check_circular=True |
循环引用检查 | False 提升速度但危险 |
json.loads() / json.load() 共用参数
参数 | 作用 | 示例 |
---|---|---|
object_hook=None |
把 dict 变自定义对象 |
实现 __complex__ |
object_pairs_hook=None |
接收有序键值对 | 实现重复键警告 |
parse_float=float |
浮点解析器 | 换成 decimal.Decimal |
parse_int=int |
整数解析器 | 换成 float |
parse_constant=None |
处理 -Infinity/Infinity/NaN |
自定义抛错 |
🧬 第三式:自定义类型完全指南
🔧 序列化 datetime
python
import json, datetime
def dt_default(o):
if isinstance(o, datetime.datetime):
return o.isoformat(timespec='seconds')
raise TypeError
log = {'time': datetime.datetime.now()}
print(json.dumps(log, default=dt_default, ensure_ascii=False))
# {"time": "2025-08-13T09:57:00"}
🔧 反序列化转回 datetime
python
def dt_hook(d):
if 'time' in d:
d['time'] = datetime.datetime.fromisoformat(d['time'])
return d
log = json.loads('{"time": "2025-08-13T09:57:00"}', object_hook=dt_hook)
print(log['time'], type(log['time']))
# 2025-08-13 09:57:00 <class 'datetime.datetime'>
🧪 第四式:命令行神器 json.tool
不用写脚本,一行命令 验证 + 美化!
bash
# 美化
$ echo '{"name":"青铜三","skills":["python","go"]}' | python -m json.tool --no-ensure-ascii
{
"name": "青铜三",
"skills": [
"python",
"go"
]
}
# 校验
$ echo '{1.2:3}' | python -m json.tool
Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
CLI 选项速查
--sort-keys
按键排序
--no-ensure-ascii
保留中文
--indent 4
指定缩进
--json-lines
每行一个 JSON
🎯 实战三板斧
实战 1:配置文件热加载
python
import json, time, os
def hot_reload(path):
last = 0
while True:
mtime = os.path.getmtime(path)
if mtime > last:
last = mtime
with open(path, encoding='utf-8') as f:
conf = json.load(f)
print('配置已热更新:', conf)
time.sleep(1)
hot_reload('config.json')
实战 2:API 单元测试断言
python
import json, requests, unittest
class TestAPI(unittest.TestCase):
def test_user(self):
resp = requests.get('https://api.xxx.com/user/1')
self.assertEqual(resp.headers['Content-Type'], 'application/json')
data = resp.json()
with open('tests/user_golden.json', encoding='utf-8') as f:
golden = json.load(f)
self.assertEqual(data, golden)
实战 3:爬虫断点续传
python
import os, json, requests, hashlib
def download(url, meta_path='meta.json'):
meta = {}
if os.path.exists(meta_path):
meta = json.load(open(meta_path))
etag = requests.head(url).headers.get('ETag')
if meta.get(url) == etag:
print('👌 已下载且未更新,跳过')
return
# 真正下载 ...
meta[url] = etag
json.dump(meta, open(meta_path, 'w'), indent=2)
🧭 常见坑位汇总
场景 | 症状 | 解决 |
---|---|---|
浮点精度 | 0.1+0.2 != 0.3 |
用 Decimal + parse_float |
NaN/Infinity | 不是合法 JSON | 设 allow_nan=False 或自定义 parse_constant |
重复键 | 只保留最后一个 | 用 object_pairs_hook 捕捉 |
超大整数 | JS 丢失精度 | 转字符串或 Decimal |
非 str 键 | 自动转 str,顺序乱 | 用 sort_keys=True 回归测试 |
🧩 类比时间:json 和异地恋
json.dumps()
= 把思念写成信 (str)json.dump()
= 把信投进邮筒 (文件)json.loads()
= 收到信读出来 (解析)json.load()
= 直接去邮筒拿信 (文件)json.default()
= 不会写的方言找翻译json.object_hook()
= 读信时脑补对象
最后感谢阅读!欢迎关注我,微信公众号:
倔强青铜三
。欢迎 点赞、收藏、关注,一键三连!!