Python 20:JSON 保存了对象的哪些部分
词频任务 "一 二 一" 的执行结果是 {"一": 2, "二": 1}。将任务快照写成 JSON,再解析回来,任务 ID、文本、标签、状态、结果和错误字段保持一致;但原来的 Task 类实例、property、方法与可变对象身份并不在 JSON 中。把“能把字典还原”误当作“复原了整个运行进程”,会让后续持久化设计建立在错误假设上。
上一章已经把模块导入与实际运行分开。本章仅在相同 tasklab 的查询快照外建立版本 1 的交换格式,不更换计算规则,也不宣称数据库或跨进程查询已实现。
交换格式是明示的字段合同
examples/python-language/src/tasklab/serialization.py 接受 task_id 与 query(store, task_id) 的快照,编码为含 version、id、kind、text、metadata、state、result、error 的 JSON 对象。version 必须恰为整数 1,布尔值 true 不等于合格的版本号;未知版本、缺字段和多字段均被拒绝。这样读写双方不会在字段含义发生变化时默认沿用旧解释。result 只能为 null 或“字符串→非负整数”的映射,整数检查特意排除了 Python 中也满足 isinstance(True, int) 的布尔值。
编码使用 json.dumps(..., ensure_ascii=False, allow_nan=False, sort_keys=True),保留中文文本并拒绝非有限浮点数。解码 json.loads 时拒绝重复字段与 NaN 之类非标准常量,再逐字段检查类型、文本的 UTF-8 字节长度和状态/结果关系。只在 succeeded 状态允许字典结果;空字典 {} 仍是已计算的空结果,不能等同于尚未计算的 null。failed 必须有错误消息,不把“成功结果 + failed 状态”当作正常历史。文本里不能编码成 UTF-8 的孤立代理码位也被拒绝;这一规则由运行时编码检查决定,不由类型提示推断。
decode_task 返回 ID 与经过校验的快照字典;当前它不实例化 Task,也不自动调用 run。因此合法数据往返指的是这些字段的值与业务含义,不是 Python 对象地址、方法绑定或资源句柄的还原。将来若有人把 JSON 放进 SQLite,必须另行决定谁负责状态迁移和提交事务,不能把 json.loads 等同于持久化。
一个好结果和几类拒绝
在 examples/python-language/ 下运行:
1 | |
正常路径提交并运行“一 二 一”,标签为“示例”,解码后的结果仍为 {"一": 2, "二": 1},状态 succeeded。反例分别删除 version,把版本换成 2,给 text 放整数、给结果计数放布尔值、把成功结果配上 cancelled 状态、添未声明字段,并直接送入畸形 JSON、重复键与 NaN。每一项都抛 TaskFormatError,实验把原始输入对应的错误记录在 examples/python-language/evidence/20/20261007-json/。累计测试还按字段循环验证所有必需项不能省、排队/成功/取消三种快照能往返;不能由此推断任意旧版数据迁移已完成。
这里明确不对外部数据使用 pickle.loads。Python 官方文档警告 pickle 可以执行任意代码,不应解封不可信数据;JSON 虽不能自动执行 Python 对象构造,也不自动安全,缺字段、错误类型、重复键、超大输入和未知任务种类仍需单独限制。本章验证了字段与状态组合,针对不可信入口的整体体积限制、路径与制品来源留到 E08;服务网络帧大小要在 34 单独验收。
预测题
某条 JSON 的 state 是 "succeeded",result 是 {};另一条也是 "succeeded",result 是 null。哪条能通过?
答案:空字典代表计算已完成但词频为空,可以通过;null 代表没有结果,与 succeeded 不一致,会被 TaskFormatError 拒绝。只检查 if result 会把这两种情况混为一谈。
变更题
把 version 改成 true,或在原始 JSON 里写两个 version 字段,Python 字典查值会有什么盲点?怎样确认拒绝实际发生?
答案:在 Python 里 True == 1,若不检查具体类型就可能误收;普通 json.loads 把重复字段合成一个字典,无法从最终字典检查发现冲突。本章先用 object_pairs_hook 拒绝重复字段,再用 type(version) is int 限定版本。分别运行 tests/test_serialization.py 的负例,检查抛出的格式错误,不把仅有注解当成验证。完整 JSON schema 迁移和历史回放仍未测。
21 将格式和 SQLite 事务结合,实际检查重启后查询与失败回滚。系列导航只连接已经验收的正文。




