I am trying to validate data with different nested $schema
values against a schema that allows it, but it doesn't correctly validate the nested objects with the correct sub schema.
Here's a simplified example:
const ajv = new Ajv();
const mainSchema = {
$id: "mainSchema",
type: "object",
properties: {
foo: {
anyOf: [{ $ref: "intSchema" }, { $ref: "strSchema" }],
},
},
};
const intSchema = {
$id: "intSchema",
type: "object",
properties: {
value: {
type: "integer",
},
},
};
const strSchema = {
$id: "strSchema",
type: "object",
properties: {
value: {
type: "string",
},
},
};
const dataA = {
$schema: "mainSchema",
foo: {
$schema: "intSchema",
value: 42,
},
};
const dataB = {
$schema: "mainSchema",
foo: {
$schema: "strSchema",
value: 1,
},
};
const dataC = {
$schema: "mainSchema",
foo: {
$schema: "unknownSchema",
value: false,
},
};
ajv.addSchema([intSchema, strSchema]);
const validate = ajv.compile(mainSchema);
console.log(validate(dataA)); // true
console.log(validate(dataB)); // true
console.log(validate(dataC)); // false
Both dataA
and dataB
return true
when I was expecting validate(dataB)
to return false. I thought it would use the $schema
value from the data to validate the object against the added schema with the corresponding $id
.
I could make a function that steps through each data object and uses the $id
property to validate it against the corresponding schema if that's the only way to accomplish this, but I assumed ajv should handle it.
As Jeremy Fiel pointed out, the validator ignores the $schema
property in the nested data type (it didn't even have an issue with an undefined $schema
being used). I played with the idea of using conditionals to pick what type of subschema to apply, but it seemed more straightforward to add validation rules to the $schema
property itself.
const ajv = new Ajv();
const mainSchema = {
$id: "mainSchema",
type: "object",
properties: {
foo: {
anyOf: [{ $ref: "intSchema" }, { $ref: "strSchema" }],
},
},
};
const intSchema = {
$id: "intSchema",
type: "object",
properties: {
$schema: {
const: "intSchema",
},
value: {
type: "integer",
},
},
};
const strSchema = {
$id: "strSchema",
type: "object",
properties: {
$schema: {
const: "strSchema",
},
value: {
type: "string",
},
},
};
const dataA = {
$schema: "mainSchema",
foo: {
$schema: "intSchema",
value: 42,
},
};
const dataB = {
$schema: "mainSchema",
foo: {
$schema: "strSchema",
value: 1,
},
};
const dataC = {
$schema: "mainSchema",
foo: {
$schema: "unknownSchema",
value: false,
},
};
ajv.addSchema([intSchema, strSchema]);
const validate = ajv.compile(mainSchema);
console.log(validate(dataA)); // true
console.log(validate(dataB)); // false
console.log(validate(dataC)); // false
The main downside to this strategy is that the $schema
property loses some flexibility and it is a bit redundant.