JSON Schema中unevaluatedProperties的精确定义与验证规则问询
一、unevaluatedProperties的设计初衷
additionalProperties: false会直接禁用所有未在schema中定义的属性,这导致一个核心问题:schema无法在不修改原定义的前提下扩展——哪怕把扩展逻辑放到allOf结构里也无效。为此,新版JSON Schema草案引入了unevaluatedProperties,专门解决schema的可扩展性问题。
二、对unevaluatedProperties规范描述的困惑解析
规范中关于unevaluatedProperties的核心描述如下:
Validation with "unevaluatedProperties" applies only to the child values of instance names that do not appear in the "properties", "patternProperties", "additionalProperties", or "unevaluatedProperties" annotation results that apply to the instance location being validated.
针对这段描述的两个困惑点,解析如下:
困惑点1:实例名称的子值是什么意思?
这里的表述属于规范的严谨性措辞,实际指的是对象实例的属性名对应的属性值。比如对象{"a": 1}中,"a"是「实例名称」(即属性名),它的「子值」就是属性值1。这句话的核心逻辑是:unevaluatedProperties只会对那些未被标记为“已评估”的属性名对应的属性值进行验证。
困惑点2:additionalProperties并未列出属性名,为何会出现在注解结果中?
关键在于JSON Schema的注解(annotation)机制:当additionalProperties执行验证时,会生成一个注解记录它所负责评估的属性范围。比如,当additionalProperties: true验证一个对象时,所有未被properties和patternProperties覆盖的属性,都会被additionalProperties标记为“已评估”,这些属性名会被纳入注解结果,unevaluatedProperties就不会再对这些属性做检查。
三、指定Schema的预期验证行为分析
先修正原Schema的语法错误(not关键字需包裹在对象中),基础Schema如下:
{ "anyOf": [ true, { "not": { "properties": { "a": true } } } ], "unevaluatedProperties": false }
以下分析完全基于JSON Schema规范定义的行为,不考虑具体验证器的实现差异。
基础Schema验证结果
针对三个目标实例:
- 实例
{}:验证通过。anyOf第一个分支true直接满足,整体anyOf通过;unevaluatedProperties: false无未评估属性需要检查。 - 实例
{"a": 1}:验证失败。anyOf仅第一个分支true通过,但该分支不会生成任何属性评估注解;属性"a"未被任何关键字标记为已评估,unevaluatedProperties: false会禁止这类未评估属性。 - 实例
{"b": 1}:验证失败。逻辑同{"a": 1},属性"b"未被评估,触发unevaluatedProperties: false的限制。
变体1:分支替换为{ "not": { "properties": { "a": false } } }
该分支逻辑:对象要么没有属性a,要么属性a的值不为false。
验证结果:
- 实例
{}:通过,同基础Schema。 - 实例
{"a": 1}:失败,同基础Schema(anyOf仅第一个分支通过,属性"a"未被评估)。 - 实例
{"b": 1}:失败,同基础Schema。
变体2:分支替换为{ "not": { "additionalProperties": false } }
该分支逻辑:对象不为空对象(因为additionalProperties: false仅允许空对象,not后则禁止空对象)。
验证结果:
- 实例
{}:通过(anyOf第一个分支true满足)。 - 实例
{"a": 1}:通过。anyOf两个分支均满足:第一个分支true通过;第二个分支中,additionalProperties: false验证{"a":1}失败,not后结果为真,分支通过。此时第二个分支的additionalProperties会生成注解,标记"a"为已评估属性,unevaluatedProperties: false不会对其做限制。 - 实例
{"b": 1}:通过。逻辑同{"a": 1},属性"b"被additionalProperties标记为已评估,不受unevaluatedProperties限制。
变体3:分支替换为{ "not": { "additionalProperties": true } }
该分支逻辑:永远不满足(因为additionalProperties: true对任何对象都验证通过,not后结果恒为假),anyOf仅第一个分支true生效。
验证结果:
- 实例
{}:通过,同基础Schema。 - 实例
{"a": 1}:失败,同基础Schema(属性"a"未被评估)。 - 实例
{"b": 1}:失败,同基础Schema。
替换为if关键字的情况
若将anyOf替换为if结构,示例如下:
{ "if": { "not": { "properties": { "a": true } } }, "then": true, "unevaluatedProperties": false }
验证逻辑:
- 实例
{}:if分支满足,then生效;无未评估属性,通过。 - 实例
{"a": 1}:if分支不满足,无then注解生成;属性"a"未被评估,unevaluatedProperties: false禁止,失败。 - 实例
{"b": 1}:if分支满足,then生效,但then未生成属性评估注解;属性"b"未被评估,失败。
内容的提问来源于stack exchange,提问作者Andreas H.

