在 JSONSchema 中,如何验证模式至少具有两个属性之一?

In JSONSchema, how do I validate a schema to have at least one of two properties?

现在已经有几个问题要求类似的东西,但我找不到与我想要的完全匹配的问题。

{
    "$schema": "http://json-schema.org/draft-07/schema",
    "title": "Example schema",
    "type": "object",
    "properties": {
        "youtubeLink": {
            "type": "string",
            "pattern": "http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?‌​[\w\?‌​=]*)?"
        },
        "assetFile": {
            "type": "string"
        }
    },
    "additionalProperties": false,
    "dependencies": {
        "assetFile": {
            "not": {
                "required": [
                    "youtubeLink"
                ]
            }
        },
        "youtubeLink": {
            "not": {
                "required": [
                    "assetFile"
                ]
            }
        }
    }
}

在示例中您可以看到两个属性,其中一个是必需的,不多也不少。目前您不能同时提供这两个属性,但您可以提供 none。我还希望没有其他属性(已经完成)。我知道这可以通过指定最少量的属性来完成,但如果您有更复杂的架构,这似乎很麻烦。

如果可能的话,我也希望有一个默认的 属性 和默认值,如果给出了两个道具中的 none。

我终于找到了一些可以工作的东西,即使是默认的。但是有没有更短的方法来做到这一点?我是在学习 JSON Schemas 并尝试了几个小时后才得出这个结果的,所以一定有更短的东西。

{
    "$schema": "http://json-schema.org/draft-07/schema",
    "title": "Config file validation",
    "description": "validation schema of the config.json file",
    "type": "object",
    "properties": {
        "youtubeLink": {
            "type": "string",
            "pattern": "http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?‌​[\w\?‌​=]*)?"
        },
        "assetFile": {
            "type": "string",
            "pattern": "^(/)?([^/\0\n]+(/)?)+\.(mp3)$"
        }
    },
    "additionalProperties": false,
    "if": {
        "not": {
            "oneOf": [
                {
                    "required": [
                        "youtubeLink"
                    ]
                },
                {
                    "required": [
                        "assetFile"
                    ]
                }
            ]
        }
    },
    "then": {
        "properties": {
            "youtubeLink": {
                "default": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
            }
        }
    },
    "dependencies": {
        "assetFile": {
            "not": {
                "required": [
                    "youtubeLink"
                ]
            }
        },
        "youtubeLink": {
            "not": {
                "required": [
                    "assetFile"
                ]
            }
        }
    }
}

I finally got something to work, even with the default. But is there any shorter way to do this?

是的,您可以使用 oneOf

https://runkit.com/embed/wqbge78zzxb6

const Ajv = require("ajv")
const ajv = new Ajv({allErrors: true})

const schema = {
  title: 'Example schema',
  oneOf: [
    {
      type: 'object',
      properties: {
        youtubeLink: {
          type: 'string'
        }
      },
      required: [
        'youtubeLink'
      ],
      additionalProperties: false
    },
    {
      type: 'object',
      properties: {
        assetFile: {
          type: 'string'
        }
      },
      required: [
        'assetFile'
      ],
      additionalProperties: false
    },
    {
      type: 'object',
      properties: {
        foo: {
          type: 'string',
          default: 'hello'
        }
      },
      additionalProperties: false
    }
  ]
}


const validate = ajv.compile(schema)

test({youtubeLink: 'hello'}) // valid
test({assetFile: 'bar'}) // valid
test({assetFile: 'bar', youtubeLink: 'hello'}) // INVALID
test({}) // default value

function test(data) {
  const valid = validate(data)
  if (valid) console.log("Valid!")
  else console.log("Invalid: " + ajv.errorsText(validate.errors))
}

你可以用oneOf表达的很干净。 oneOf 断言给定模式中只有一个必须有效才能验证模式。

{
  "type": "object",
  "properties": {
    "youtubeLink": {},
    "assetFile": {}
  },
  "additionalProperties": false,
  "oneOf": [
    { "required": ["youtubeLink"] },
    { "required": ["assetFile"] }
  ]
}
  • 如果您只有“youtubeLink”,/oneOf/0 会通过,/oneOf/1 会失败。 (通过)
  • 如果您只有“assetFile”,/oneOf/0 会失败,/oneOf/1 会通过。 (通过)
  • 如果两者都有,/oneOf/0 将通过,/oneOf/1 将通过。 (失败)
  • 如果你有none,/oneOf/0会失败,/oneOf/1也会失败。 (失败)