Search code examples
jsonjsonschema

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


Now there are a few questions already asking for similar things, but I couldn't find one that matches exactly what I want.

{
    "$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"
                ]
            }
        }
    }
}

In the example you can see two properties and at one of them is required, no more, no less. At the moment you can't give in both properties, but you can give in none. I also want there to be no additional properties (which is already done). I know that it can be done by specifying a minimal amount of properties, but does seem hacky if you have a more complicated schema.

And if it is possible, I would also prefer there to be a default property with a default value, if none of the two props are given.


Solution

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

    Yes, you can use 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))
    }