Home > Net >  Json Schema oneOf , not working as expected
Json Schema oneOf , not working as expected

Time:07-11

I am creating a json schema with the intention of either producing a single array of product codes or arrays of arrays of product codes. In short, I want to be able to return as a result either productCodes: [12224, 444444] or productCodesArray: [[12222,55555],[11111,99999]] . I attempted a solution which partially works but allows both to. be allowed. I may have misread the spec. How can I achieve this? I tried using the oneOf and here is my solution:

             {
                 "$schema": "http://json-schema.org/draft-07/schema#",
                  "$id":"https://examples.com/schemas/my-array-schema",
                  "type":"object",
                  "required": ["productCodesResult"],
                  "properties": {
                  "additionalItems": false,
                  "productCodesResult": {
                  "type": "object",
                  "properties": {
                  "oneOf": {
                       "reasons": {
                       "$ref": "#/definitions/productCodesDef"
                   },
                      "reasonsArray": {
                      "$ref": "#/definitions/productCodesArrayDef"
                  }
                }
               }
              }
             },
                 "definitions": {
                 "productCodesDef": {
                 "type": "object",
                  "required": ["productCodes"],
                  "properties":{
                  "productsIds": {
                  "type": "array",
                  "items": {
                  "type": "integer",
                  "minItems": 1
                          }
                    }
                  }.   
                },
                    "productCodesArrayDef": {
                    "type": "object",
                    "required": ["reasonsArray"],
                    "properties":{
                    "productsCodesArray": {
                    "type": "array",
                    "items": {
                    "type": "array",
                    "items":{
                    "type": "integer",
                    "minItems": 1
                   }
                 }
               }
              }
            }
           }
         }

CodePudding user response:

Tested this with Ajv. Specify oneOf on the level of the productCodes property. Add two options, one that accepts an array of strings (string[]) and the other one that accepts an array of array of strings (string[][]).

const schema = {
  type: 'object',
  required: ['productCodes'],
  properties: {
    productCodes: {
      type: 'array',
      oneOf: [
        {
          type: 'array',
          minItems: 1,
          items: {
            type: 'string',
          },
        },
        {
          type: 'array',
          minItems: 1,
          items: {
            type: 'array',
            minItems: 1,
            items: {
              type: 'string',
            },
          },
        },
      ],
    },
  },
};

No need to give the properties different names, just call it productCodes. In the example above I made it required so passing an empty object will not be valid.

import Ajv from 'ajv';

const data = {
  // productCodes: ['123', '345'],
  productCodes: [
    ['123', '345'],
    ['123', '345'],
  ],
};

const validate = this.ajv.compile(schema);
const valid = validate(data);
console.log(valid);

If for whatever requirement the properties must have different names, then define them as 2 different properties. Using oneOf on the root level you can make them mutually exclusive. Either productCodes or productCodesArray must be present.

const schema = {
  type: 'object',
  required: [],
  properties: {
    productCodes: {
      type: 'array',
      minItems: 1,
      items: {
        type: 'string',
      },
    },
    productCodesArray: {
      type: 'array',
      minItems: 1,
      items: {
        type: 'array',
        minItems: 1,
        items: {
          type: 'string',
        },
      },
    },
  },
  oneOf: [
    { type: 'object', required: ['productCodes'] },
    { type: 'object', required: ['productCodesArray'] },
  ],
};

As an alternative you can also do this with allOf and not.

const schema = {
  type: 'object',
  required: [],
  properties: {
    productCodes: { ... }
    productCodesArray: { ... }
  },
  allOf: [
    {
      not: {
        type: 'object',
        required: ['productCodes', 'productCodesArray'],
      },
    },
  ],
};

  • Related