字段存在却仍然报空值:把数据标准落成 JSON Schema 契约
某制造企业接入设备台账,接口文档写着“设备编码必填,安装位置可空”。上线后,下游却同时收到字段缺失、显式 null 和空字符串。三种输入进入同一清洗分支,最后都变成“未知”,无法判断究竟是设备尚未安装,还是采集程序漏传字段。这个场景中的数据均为构造样例,问题来自契约没有区分存在性与取值范围。
先把争议变成四条样本
我会先要求生产方和消费方共同确认四条输入的预期,而不是继续修改文字说明。设备编码缺失应拒绝,编码为空字符串应拒绝;安装位置允许显式 null,但字段本身必须出现。这让“尚未采集位置”和“接口没有实现位置字段”成为两种可观察状态。
JSON Schema 的 properties 用于描述字段,单独声明它不会要求字段出现;required 检查字段存在,却不会自动禁止 null。是否允许 null 要由字段的 type 决定。这个区分可在官方对象类型说明核实。本文采用 Draft 2020-12,避免校验器自行选择其他草案。
用一段程序固定语义
下面示例适用于 Python 3 和 jsonschema 4.x。保存为 contract_check.py,安装依赖后执行即可。它同时校验契约自身与输入,输出的布尔值依次为真、假、假、假。
python -m pip install 'jsonschema>=4,<5'
python contract_check.py
from jsonschema import Draft202012Validator
schema = {
"$schema": "hxxps://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"device_code": {"type": "string", "minLength": 1},
"install_location": {"type": ["string", "null"]},
},
"required": ["device_code", "install_location"],
"additionalProperties": False,
}
Draft202012Validator.check_schema(schema)
validator = Draft202012Validator(schema)
samples = [
{"device_code": "D001", "install_location": None},
{"device_code": "D001"},
{"device_code": "", "install_location": None},
{"device_code": "D001", "install_location": None,
"install_locaton": "A区"},
]
for index, sample in enumerate(samples, 1):
errors = list(validator.iter_errors(sample))
print(index, not errors)
for error in errors:
print(list(error.path), error.validator, error.message)
check_schema 与 iter_errors 的接口来自校验器官方文档。前者应在加载规则时调用;后者可以收集一条记录的多个错误,便于集中反馈。不要在错误信息中直接拼接完整原始报文,否则设备位置等字段也会进入公共日志。
空字符串和纯空白还差一步
minLength: 1 能拒绝空字符串,但不能拒绝只有空格的编码。这里必须先决定编码的标准化责任:如果允许清洗,先去除首尾空白,再校验,并保留原值与处理结果;如果禁止清洗,就把纯空白作为拒绝条件。不要让各接入任务分别决定,否则同一个编码可能在两个主题域得到不同主键。
本例故意不自动填充安装位置。把缺失字段补成 null 会抹掉契约违约证据,也使后续无法计算“字段是否按约定传输”的指标。实际落地时,我会把缺失率、显式空值率、非法类型率拆开统计,并以生产方版本和采集批次为维度观察。
严格字段集合怎样发布
additionalProperties: false 会拒绝未声明字段,这能发现样例中的拼写错误,但也意味着新增字段不能随意提前上线。它适合字段集合已经协商一致的入口;如果生产方允许独立扩展,应设计扩展对象或新的契约版本,而不是在故障时临时关闭所有检查。
建议把契约文件与正反例一起进入代码评审。一次字段调整至少回答三个问题:旧数据能否通过新规则,新数据能否被旧消费者接受,失败记录由谁处理。把位置字段从允许 null 改成只允许字符串,即使字段名称完全不变,也会改变已有输入的合法性。
验收不只看合法样本通过。必须把缺字段、空字符串、错误类型与拼写错误分别送入测试入口,确认失败原因能回到对应规则,同时确认隔离区保留了可追踪的批次编号。这样才能证明契约真正执行,而非只是保存了一份 JSON 文件。
规则失败之后怎样闭环
隔离记录需要保存规则版本和失败路径,但不必把全部字段复制到告警通知中。值班人员先按批次看失败类别,再由具备权限的处理程序定位原始输入。这样既能追查漏传,也能避免排障日志变成第二份业务数据库。
当生产方修复后,重放必须继续使用原批次对应的契约,或者明确登记规则迁移。否则同一份历史输入今天失败、明天通过,平台却无法解释变化来自数据修复还是规则放宽。每次规则发布都附上正反样本,可以把这种差异提前暴露在评审阶段。
小结
必填与非空是两条独立约束。先明确缺失、null、空字符串的业务含义,再把存在性、类型与字段集合写入可执行契约,数据标准才能在接口变更时提供明确、可回归的判断依据。
- 点赞
- 收藏
- 关注作者
评论(0)