Search code examples
javascriptarraysjsonjsonschemaajv

Requiring certain elements to exist in an array using AJV


I would like to validate a JSON object using a schema to ensure that all the elements specified in the schema exist in the array. For example, my people array MUST contain these people:

{
  "people":[
    {
      "name":"Bob",
      "age":"26"
    },
    {
      "name":"Jim",
      "age":"35"
    },
    {
      "name":"John",
      "age":"27"
    }
  ]
}

Any additional people are ignored but these have to exist. I've tried to use allOf in the items specifier but it fails since allOf specifies that each element should match each of the provided schemas.

{
  "people":{
    "type":"array",
    "items":{
      "allOf":[
        {
          "type":"object",
          "properties":{
            "name":{
              "type":"string",
              "const":"Bob"
            },
            "age":{
              "type":"string",
              "const":"26"
            }
          },
          "required":[
            "name",
            "age"
          ]
        },
        {
          "type":"object",
          "properties":{
            "name":{
              "type":"string",
              "const":"Jim"
            },
            "age":{
              "type":"string",
              "const":"35"
            }
          },
          "required":[
            "name",
            "age"
          ]
        },
        {
          "type":"object",
          "properties":{
            "name":{
              "type":"string",
              "const":"John"
            },
            "age":{
              "type":"string",
              "const":"27"
            }
          },
          "required":[
            "name",
            "age"
          ]
        }
      ]
    }
  }
}

With this information, we can conclude that the valid people array would be:

  [
    {
      "name":"Bob",
      "age":"26"
    },
    {
      "name":"Jim",
      "age":"35"
    },
    {
      "name":"John",
      "age":"27"
    },
    {
      "name":"Tim",
      "age":"47"
    }
  ]

and an invalid array would be:

  [
    {
      "name":"Bob",
      "age":"26"
    },
    {
      "name":"Jim",
      "age":"35"
    },
    {
      "name":"Tim",
      "age":"47"
    }
  ]

since it's missing John from the list.

Here is the code for the AJV file.

const Ajv = require('ajv');
const jsonInput = require('./people.json');
const schema = require('./peopleSchema.json');

const ajv = new Ajv();
const validate = ajv.compile(schema);
const valid = validate(jsonInput);

console.log(valid);

if (!valid) console.log(validate.errors);

What property would I use to specify which elements are required in an array as stated above?

Edit: Based on the comment, I tried to use contains and haven't had any luck.

{
  "$id": "root",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "people": {
      "type": "array",
      "items": {
        "type": "object",
        "allOf": [
          {
            "contains": {
              "properties": {
                "name": {
                  "enum": ["Bob"]
                },
                "age": {
                  "enum": ["26"]
                }
              }
            }
          },
          {
            "contains": {
              "properties": {
                "name": {
                  "enum": ["John"]
                },
                "age": {
                  "enum": ["27"]
                }
              }
            }
          },
          {
            "contains": {
              "properties": {
                "name": {
                  "enum": ["Jim"]
                },
                "age": {
                  "enum": ["36"]
                }
              }
            }
          }
        ]
      }
    }
  },
  "required": ["people"]
}

Solution

  • I was able to get my requirements met by using the following as a schema.

    {
      "properties": {
        "people": {
          "items": {
            "properties": {
                "name": { "type": "string" },
                "age": { "type": "string" }
            }
          },
          "allOf": [
            {
              "contains": {
                "const": {
                    "name": "Bob",
                    "color": "26"
                }
              }
            },
            {
              "contains": {
                "const": {
                    "name": "Jim",
                    "color": "35"
                }
              }
            },
            {
              "contains": {
                "const": {
                    "name": "John",
                    "color": "27"
                }
              }
            }
            ]
        }
      },
      "required": [
        "people"
      ],
      "additionalProperties": false
    }