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'],
},
},
],
};