danday74
danday74

Reputation: 57271

JSON Schema / AJV array item must be in another array

I am using Node's AJV (which enforces JSON Schema).

I want to validate array1 properties.bars. Easy enough.

Then I want to ensure that an item in array2 properties.keep is in array1 properties.bars.

How do I do this?

I've got:

const config = require('../../../config')
const VALID_BARS = Object.keys(config.LHS_RHS_LOOKUP)

const schemaItems = {
  id: 'schemaItems',
  type: 'string',
  anyOf: [
    { enum: VALID_BARS },
    { pattern: '^[^\\s]+ [^\\s]+$' }
  ]
}

const schemaOptions = {
  type: 'object',
  properties: {
    bars: {
      type: 'array',
      default: [VALID_BARS[0]],
      items: schemaItems,
      minItems: 1,
      uniqueItems: true
    },
    keep: {
      type: 'array',
      default: [],
      items: schemaItems, // << THIS NEEDS TO CHANGE
      minItems: 0,
      uniqueItems: true
    },
    protect: {
      default: true,
      type: 'boolean'
    }
  }
}

module.exports = schemaOptions

Upvotes: 2

Views: 1758

Answers (1)

Roy Reiss
Roy Reiss

Reputation: 993

You would want to use a $data pointer to the first array. Right now it's just a proposal. It allows you to assign a value to a keyword using the data value of another property.

So in this case the items property of your second array will have an enum property that will use the $data value of the first array.

In order to do this I had to remove the 'anyOf' in your original schema so that the first array wouldn't end up referring to itself. I also combined the schemaItems into the main schema via a $ref and definition.

Here's a PLNKR of it in action.

The test code would look something like:

let schema = {
  definitions: {
    schemaItems: {
      id: 'schemaItems',
      type: 'string',
      pattern: '^[^\\s]+ [^\\s]+$'
    }
  },
  type: 'object',
  properties: {
    bars: {
      type: 'array',
      items: {
        $ref: "#/definitions/schemaItems"
      },
      minItems: 1,
      uniqueItems: true
    },
    keep: {
      type: 'array',
      items: {
        type: 'string',
        enum: {
          "$data": "/bars"
        }
      },
      minItems: 0,
      uniqueItems: true
    },
    protect: {
      default: true,
      type: 'boolean'
    }
  }
};

And a valid data sample would be:

let data = {
  bars: [
    "d d",
    "b b"
  ],
  keep: [
    "d d"
  ],
  protect: true
};

Upvotes: 3

Related Questions