我有以下模式,我为有效负载选择两个子模式之一。
{
"type": "object",
"definitions": {
"XXXTrades": {
"type": "object",
"title": "XXX Trades",
"properties": {
"instrumentTypes": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
},
"oneOf": [
{
"properties": {
"books": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
},
"required": [
"books"
]
},
{
"properties": {
"tradeLegIds": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 1
}
},
"required": [
"tradeLegIds"
]
}
],
"additionalProperties": false
},
"YYYYTrades": {
"type": "object",
"title": "YYYY Trades",
"properties": {
"sourceType": {
"type": "string",
"enum": [
"YYYY"
]
},
"folioInstrumentFilter": {
"type": "string"
}
},
"anyOf": [
{
"type": "object",
"title": "YYYY Folios",
"properties": {
"folios": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 1
}
},
"required": [
"folios"
]
},
{
"title": "YYYY Trade Ids",
"type": "object",
"properties": {
"YYYYTradeIds": {
"type": "array",
"items": {
"type": "integer"
}
},
"minItems": 1
},
"required": [
"YYYYTradeIds"
]
}
],
"required": [
"sourceType"
],
"allOf": [
{
"if": {
"properties": {
"folioInstrumentFilter": {
"type": "string"
}
},
"required": [
"folioInstrumentFilter"
]
},
"then": {
"required": [
"folios"
]
}
}
],
"additionalProperties": false
}
},
"anyOf": [
{
"$ref": "#/definitions/YYYYTrades"
},
{
"$ref": "#/definitions/XXXTrades"
}
]
}
但是以下有效负载无法验证
{
"YYYYTradeIds": [
1
],
"sourceType": "YYYY"
}
出现以下验证错误
{
"errors": [
{
"instancePath": "",
"schemaPath": "#/definitions/YYYYTrades/additionalProperties",
"keyword": "additionalProperties",
"params": {
"additionalProperty": "YYYYTradeIds"
},
"message": "must NOT have additional properties",
...
...
..
{
"instancePath": "",
"schemaPath": "#/definitions/XXXTrades/oneOf",
"keyword": "oneOf",
"params": {
"passingSchemas": null
},
"message": "must match exactly one schema in oneOf",
...
...
]
}
验证器 AJV8 似乎无法确定在验证有效负载时使用哪个子模式。是否有任何机制可以强制解析器/验证器应用正确的子模式?
我尝试内联模式而不是使用定义,在 sourceType 上添加条件,但它不起作用。我已经在网上寻找解决方案几天了。
您的架构中有一个常见错误。
additionalProperties: false
和组合关键字(例如 anyOf
和 oneOf
)在早期版本的 JSON Schema 中不能很好地协同工作。 我假设您使用的是 Draft-07,这是默认的 AJV 8 实现。
要使此行为正常工作,您需要在根的子模式中定义关键字,使用空模式
{}
或true
就足够了。
对于
if, then
,如果您已经在其他地方定义了 type
,通常应该使用模式约束而不仅仅是 type
。否则,对于您所拥有的用例来说,需要进行大量额外的处理和语法,即如果存在另一个属性,则强制执行特定属性。这可以通过 dependencies
来完成。
我对您的架构所做的更新,
true
allOf>if>then
约束替换为 dependencies
minItems
子模式中错位了
YYYYTradeIds
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"definitions": {
"XXXTrades": {
"type": "object",
"title": "XXX Trades",
"properties": {
"instrumentTypes": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
},
"books": true,
"tradeLegIds": true
},
"oneOf": [
{
"properties": {
"books": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
},
"required": [
"books"
]
},
{
"properties": {
"tradeLegIds": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 1
}
},
"required": [
"tradeLegIds"
]
}
],
"additionalProperties": false
},
"YYYYTrades": {
"type": "object",
"title": "YYYY Trades",
"properties": {
"sourceType": {
"type": "string",
"enum": [
"YYYY"
]
},
"folioInstrumentFilter": {
"type": "string"
},
"folios": true,
"YYYYTradeIds": true
},
"anyOf": [
{
"type": "object",
"title": "YYYY Folios",
"properties": {
"folios": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 1
}
},
"required": [
"folios"
]
},
{
"title": "YYYY Trade Ids",
"type": "object",
"properties": {
"YYYYTradeIds": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 1
}
},
"required": [
"YYYYTradeIds"
]
}
],
"required": [
"sourceType"
],
"dependencies": {
"folioInstrumentFilter": [
"folios"
]
},
"additionalProperties": false
}
},
"anyOf": [
{
"$ref": "#/definitions/YYYYTrades"
},
{
"$ref": "#/definitions/XXXTrades"
}
]
}
如果您有兴趣并愿意迁移到较新的 JSON 架构草案,则可以使用较新的关键字
additionalProperties
来避免架构根部的 unevaluatedProperties
问题和重复定义。 与 additionalProperties
不同,此关键字可以查看组合(allOf、oneOf、anyOf)关键字中定义的子模式。
2020-12 草案需要进行一些其他更改
$schema
需要正确的版本definitions
更新为$defs
additionalProperties
更新为unevaluatedProperties
const Ajv = require('ajv/dist/2020');
const ajv = new Ajv({ strict: false });
let instance = // your json data instance
let schema = // your json schema
try {
const validate = ajv.compile(schema)
const valid = validate(instance)
console.log(results = { valid: valid, error: validate.errors })
} catch ({ message }) {
console.error(message)
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"$defs": {
"XXXTrades": {
"type": "object",
"title": "XXX Trades",
"properties": {
"instrumentTypes": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
},
"oneOf": [
{
"properties": {
"books": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
},
"required": [
"books"
]
},
{
"properties": {
"tradeLegIds": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 1
}
},
"required": [
"tradeLegIds"
]
}
],
"unevaluatedProperties": false
},
"YYYYTrades": {
"type": "object",
"title": "YYYY Trades",
"properties": {
"sourceType": {
"type": "string",
"enum": [
"YYYY"
]
},
"folioInstrumentFilter": {
"type": "string"
}
},
"anyOf": [
{
"type": "object",
"title": "YYYY Folios",
"properties": {
"folios": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 1
}
},
"required": [
"folios"
]
},
{
"title": "YYYY Trade Ids",
"type": "object",
"properties": {
"YYYYTradeIds": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 1
}
},
"required": [
"YYYYTradeIds"
]
}
],
"required": [
"sourceType"
],
"dependentRequired": {
"folioInstrumentFilter": [
"folios"
]
},
"unevaluatedProperties": false
}
},
"anyOf": [
{
"$ref": "#/$defs/YYYYTrades"
},
{
"$ref": "#/$defs/XXXTrades"
}
]
}