上周给一份配置补校验,schema 写完,我随手拿一条脏数据丢进去,全过。
不是校验器坏了,是我关键字写歪了。下面这几条是我这两周自己跑出来的,环境是 Python 的 jsonschema 库、规则用 Draft 2020-12,每一条都贴了实际结果,你可以在自己机器上跑一遍。

一、format 写了,不等于会拦
{"type":"string","format":"email"},丢一个 "not-an-email" 进去,校验错误数是 0。
2020-12 里 format 默认只算注解,校验器不会拿它做断言。想真拦住,要么自己写 pattern,要么在构造校验器的时候显式打开 format 检查——我用 Draft202012Validator(schema, format_checker=FormatChecker()) 再跑一次,同一条数据就从 0 个错误变成 1 个错误。
这条特别会骗自己:schema 看着挺严,实际什么都没拦。
二、array 不写 items,等于什么都收
{"type":"array"},塞 [1, {"a": 2}, null],错误 0。
只要写了 type array,元素长什么样它就不管了。要管就得写 items,元素不止一种就 items+prefixItems。
三、required 里的字段,properties 里没有
我写成 "required": ["id", "nickname"],properties 里只定义了 id。
传 {"id": 1, "nickname": "x"},过。传 {"id": 1},报 'nickname' is a required property。
看着是能拦住漏字段,其实 nickname 的类型、长度、格式一样都没约束——因为 properties 里根本没定义它。必填有了,校验没跟上。这是我上一版挺典型的一处白写。
四、pattern 里的点没转义
"pattern": "^v1.0$",丢 "v1x0" 进去,过。
正则里的点匹配任意字符,写成 ^v1\.0$ 才管用。版本号、日期这种带点的串,这个坑踩一次能记很久。
五、数字约束没写 type
{"minimum": 18},丢字符串 "18" 进去,过。
minimum 和 maximum 只对数字生效,类型不锁住,等于没写。要同时管类型和范围,type 和 minimum 得一块写。
顺手说两个默认值。
additionalProperties 默认是 true。你不写,多传的字段就是合法的。我在顶层写 false 卡死以后,代价是所有没在 properties 里的字段都会被拦,包括自己临时加的调试字段——要么全列进去,要么就别开这个开关。
minLength 数的是字符数不是字节。"中文五个字" 是 5 个字符,minLength 6 会直接报 too short。
那我现在怎么少踩
不靠背关键字,改成写完自己回头验一遍:先把 schema 生成出来,再拿一份示例数据过一遍校验。示例过不了,基本就是 schema 写歪了,而不是数据脏。

我自己那套生成脚本最后一步就是干这个,生成完拿 example 回头 validate 一次。有个坑得提前说清:那个环境里要是没装 jsonschema,脚本只会打一行提示然后跳过校验,看着是跑完了,其实一点没验。


仓库在这儿,MIT,脚本是纯标准库的,不装依赖也能跑生成那部分: