如何在JSON Schema中实现数值属性的最小/最大值依赖于其他属性?
关于JSON Schema动态依赖属性范围约束的解决方案
你猜的没错,标准JSON Schema(包括最新的Draft 2020-12)确实没办法直接写出你想要的这种动态范围约束——minimum和maximum这类关键字只能接受固定数值,没办法引用实例中其他属性的值来做实时计算。你提到的if-then-else功能只能基于属性的固定值或静态范围触发分支,无法处理“基于另一个属性的动态计算结果”这类场景。
不过根据你的需求,还是有几种可行的替代方案:
方案1:针对离散值使用allOf+if-then(纯标准Schema)
如果foo的可能取值是有限的离散值(比如你这里是10-20的整数,共11个可选值),可以通过allOf配合多个if-then块逐个覆盖每个foo值对应的bar范围:
{ "type": "object", "properties": { "foo": { "type": "integer", "minimum": 10, "maximum": 20 }, "bar": { "type": "integer" } }, "allOf": [ { "if": { "properties": { "foo": { "const": 10 } } }, "then": { "properties": { "bar": { "minimum": 6, "maximum": 8 } } } }, { "if": { "properties": { "foo": { "const": 15 } } }, "then": { "properties": { "bar": { "minimum": 9, "maximum": 12 } } } }, { "if": { "properties": { "foo": { "const": 20 } } }, "then": { "properties": { "bar": { "minimum": 12, "maximum": 16 } } } } // 继续为foo的其他取值(11-14、16-19)添加对应的if-then约束 ] }
注意:这种方式的局限性很明显——如果foo的取值范围很大(比如0-1000的整数)或者是浮点数,写起来会非常繁琐,几乎不现实。
方案2:借助验证器扩展实现自定义逻辑
如果可以灵活选择JSON Schema验证器(比如主流的Ajv),你可以通过自定义关键字来实现动态范围计算,这样就能在Schema里统一管理所有约束。
以Ajv为例,你可以注册一个自定义关键字(比如rangeDependsOn),然后编写验证逻辑来计算bar的动态范围:
1. 定义带自定义关键字的Schema
{ "type": "object", "properties": { "foo": { "type": "integer", "minimum": 10, "maximum": 20 }, "bar": { "type": "integer", "rangeDependsOn": { "property": "foo", "minMultiplier": 0.6, "maxMultiplier": 0.8 } } } }
2. 在Ajv中注册自定义关键字的验证逻辑
const Ajv = require('ajv'); const ajv = new Ajv(); ajv.addKeyword('rangeDependsOn', { validate: (schema, barValue, _, __, parentData) => { const fooValue = parentData.foo; // 如果foo不合法,基础Schema校验会先报错,这里直接返回true即可 if (typeof fooValue !== 'number') return true; // 根据需求调整取整逻辑(比如向下取整、向上取整或四舍五入) const min = Math.floor(fooValue * schema.minMultiplier); const max = Math.floor(fooValue * schema.maxMultiplier); return barValue >= min && barValue <= max; }, schemaType: 'object' }); // 编译Schema并验证实例 const validate = ajv.compile(yourSchema); const isValid = validate(yourConfig);
总结
- 如果必须严格遵循标准JSON Schema,且
foo的取值是有限离散值,方案1可以满足需求;否则只能通过外部逻辑完成动态范围校验。 - 如果可以使用支持扩展的验证器,方案2是最优雅的解决方式,能把所有约束统一在Schema中管理。
内容的提问来源于stack exchange,提问作者Carlos3dx
相关产品推荐
相关产品推荐

