AI 返回的 JSON,先查重复键再往下传
下面这段有两个 summary,Python 默认读取时不会报错,前一个值直接没了:
import json
text = '{"summary":"第一版","summary":"第二版","tags":[]}'
print(json.loads(text))
# {'summary': '第二版', 'tags': []} 如果这是准备存下来的 AI 摘要,重复字段就这么被悄悄覆盖了。默认读取还接受 NaN、Infinity 这些非标准常量。能读成 Python 对象,离符合你要的字段约定还差一步。
可以把这一步放在接收端:先拒绝重复键和非标准常量,再检查字段。这里做了一个只用标准库的小工具,Python 3.9+ 能运行。
下载完整示例包 strict-json-result-check.zip
压缩包里有完整脚本、正常和错误样例、验证程序及中文说明。单独的脚本也可以下载:下载 strict_result.py
解压后先跑一次
进入解压目录:
python3 strict_result.py good.json 正常样例长这样:
{
"summary": "这是一份用于演示结构校验的本地样例。",
"tags": ["JSON", "Python"]
} 应该输出 VALID,退出码为 0。这个例子的字段约定很小:
- 顶层只允许
summary和tags,两个都要有。 summary是字符串,去掉首尾空白后长 1–120 个字符。tags是 0–5 个字符串组成的数组,每项去空白后长 1–20 个字符,不能重复;比较区分大小写。
换成重复键样例再试:
python3 strict_result.py bad-duplicate.json 这次退出码为 2,错误写到 stderr:
INVALID: duplicate object key: 'summary' 实际使用时,把文件名换成已提取出来的业务 JSON 文本即可。脚本不自动删 Markdown 围栏,也不会悄悄补括号;返回格式不对就报告给调用方处理。输入限 64 KiB、UTF-8,超过上限直接拒绝。
严格读取的关键在这两个钩子
import json
def unique_object(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError(f"duplicate key: {key}")
result[key] = value
return result
def reject_constant(token):
raise ValueError(f"nonstandard constant: {token}")
data = json.loads(text, object_pairs_hook=unique_object,
parse_constant=reject_constant) object_pairs_hook 在对象变成字典之前检查键,因此能发现嵌套对象中的重复项。parse_constant 用来拒绝 NaN 和正负 Infinity。只设 strict=True 解决不了这两个问题;allow_nan=False 是编码时的选项,也不能直接拿来当 loads() 的解码参数。
上面是核心用法;完整文件另外处理了字节上限、UTF-8、错误位置和字段校验。它只验这份摘要结构,不是通用 JSON Schema 工具。检查时去空白不会改写原文件,VALID 也不代表摘要事实正确、标签选得合理。
附件还带了一组验证:
python3 verify.py 预期输出 PASS 32/32。这组用例已在 Python 3.12.14 实际运行,并从压缩包解压后再跑过,包含重复键、非标准常量、错误类型、多余字段、围栏、超长输入和深层嵌套。输入是本地构造的样例,没调用模型。
如果接口支持严格结构化输出,优先用它约束返回结构;拒绝响应和截断要在提取业务文本前按接口协议处理,这个脚本不负责。这里适合普通 JSON 文本、历史文件或不同客户端导出的结果,在交给下游前再验一次自己的字段约定。
参考:Python 官方 json 模块文档;OpenAI Structured Outputs 指南,2026 年 10 月 21 日核对。