如何在JSON Schema中添加自定义约束、字段及跨层验证支持?
基于JSON Schema的跨层数据验证扩展方案
需求实现完整示例
先给出整合所有需求的最终Schema,再拆解每个需求的实现逻辑:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "myId", "$vocabulary": { "https://your-domain.com/vocab/ui-directives": true, "https://your-domain.com/vocab/model-directives": true }, "type": "object", "properties": { "code": { "type": "string", "pattern": "[a-zA-Z0-9]{6}-[0-9]{4}", "title": "编码", "uiDirectives": { "inputType": "text", "placeholder": "请输入6位字母数字+4位数字的编码,格式如XXX-XXXX", "maxLength": 11 }, "modelDirectives": { "dbColumn": "code", "dataType": "varchar(11)" } }, "firstAmount": { "$ref": "#/$defs/amount", "title": "第一金额", "uiDirectives": { "inputType": "number", "placeholder": "请输入1-9999999之间的整数", "min": 1, "max": 9999999 }, "modelDirectives": { "dbColumn": "first_amount", "dataType": "int(7)" } }, "secondAmount": { "$ref": "#/$defs/amount", "title": "第二金额", "uiDirectives": { "inputType": "number", "placeholder": "请输入小于第一金额的整数", "min": 1, "max": 9999999 }, "modelDirectives": { "dbColumn": "second_amount", "dataType": "int(7)" } }, "inputDate": { "$ref": "#/$defs/dateTime", "title": "输入日期", "uiDirectives": { "inputType": "date", "min": "2020-01-01" }, "modelDirectives": { "dbColumn": "input_date", "dataType": "date" } } }, "required": ["code", "inputDate"], "dependentRequired": {"firstAmount": ["secondAmount"]}, "additionalProperties": false, "$expr": { "$lt": [{"$data": "/secondAmount"}, {"$data": "/firstAmount"}] }, "errorMessage": { "$expr": "第二金额必须小于第一金额", "required": { "code": "编码为必填项", "inputDate": "输入日期为必填项" }, "dependentRequired": "如果填写第一金额,则必须填写第二金额" }, "$defs": { "amount": {"type": "integer", "minimum": 1, "maximum": 9999999}, "dateTime": {"type": "string", "format": "date"} } }
各需求拆解实现
1. 添加secondAmount < firstAmount的约束
使用JSON Schema 2020-12标准的$expr关键字,结合$data引用其他字段的值,直接在根对象中添加表达式逻辑:
"$expr": { "$lt": [{"$data": "/secondAmount"}, {"$data": "/firstAmount"}] }
依赖dependentRequired规则,确保只有firstAmount存在时secondAmount才会被验证,避免空值报错。
2. 添加自定义错误信息
使用验证器支持的errorMessage扩展关键字(如AJV 8+内置支持),为每个约束指定友好提示:
"errorMessage": { "$expr": "第二金额必须小于第一金额", "required": { "code": "编码为必填项", "inputDate": "输入日期为必填项" } }
如果需要标准兼容的注释,可使用$comment字段补充,但自定义错误提示通常依赖验证器扩展。
3. 为字段添加前端展示标签
优先使用JSON Schema标准的title字段,直接作为前端展示的标签:
"code": { ..., "title": "编码" }
如需多语言支持,可定义自定义labels关键字,并通过$vocabulary声明:
"$vocabulary": { "https://your-domain.com/vocab/labels": true }, "code": { ..., "labels": { "zh-CN": "编码", "en-US": "Code" } }
4. 为模型层和展示层添加指令
定义两个自定义关键字uiDirectives(前端展示)和modelDirectives(模型/数据库层),并在$vocabulary中声明其语义URI:
"$vocabulary": { "https://your-domain.com/vocab/ui-directives": true, "https://your-domain.com/vocab/model-directives": true }, "firstAmount": { ..., "uiDirectives": { "inputType": "number", "placeholder": "请输入1-9999999之间的整数" }, "modelDirectives": { "dbColumn": "first_amount", "dataType": "int(7)" } }
工具链可通过解析这些关键字,自动生成前端表单配置或数据库表结构。
5. 验证自定义约束
- 无需扩展验证器的场景:使用标准
$expr实现的约束,只要验证器支持JSON Schema 2020-12版本(如AJV 8+、JSON Schema Validator),即可直接验证。 - 需要扩展验证器的场景:如果自定义了新的验证关键字(如自定义
lessThan),则需扩展验证器添加逻辑。以AJV为例:
const ajv = new Ajv(); ajv.addKeyword({ keyword: 'lessThan', type: 'number', schemaType: 'string', // 指向目标字段的路径 validate: (schema, data, parentData) => data < parentData[schema], errorMessage: '必须小于{schema}字段的值' });
自定义词汇表操作流程
- 声明词汇表:在Schema根对象添加
$vocabulary,指定自定义词汇的URI和是否强制实现(true表示工具必须处理该词汇)。 - 定义关键字语义:明确自定义关键字的用途(如
uiDirectives用于前端渲染配置),形成内部规范。 - 工具链适配:修改模型生成工具、前端解析逻辑,读取并应用这些自定义关键字。
内容的提问来源于stack exchange,提问作者Domenico Lorusso
相关产品推荐
相关产品推荐

