Inlining schemas with json-schema-ref-parser is unnecessary, and may not work in case inlined schemas have $id
See original GitHub issueI think this might be a V7 bug as my schemas were all passing with V6.
If an external schema is referenced using $ref
by more than one property, or within a property defined in the definitions
section, and the $refs
are resolved by putting the schemas inline, as with json-schema-ref-parser, then compilation fails with the error
reference "https://schemas.example.com/samples/child" resolves to more than one schema
What version of Ajv are you using? Does the issue happen if you use the latest version? 7.0.4
Ajv options object {}
JSON Schema
samples/parent.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://schemas.example.com/samples/parent",
"title": "Schema that references another schema multiple times",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"eldestChild": {
"$ref": "./child.schema.json"
},
"youngestChild": {
"$ref": "./child.schema.json"
}
}
}
samples/child.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://schemas.example.com/samples/child",
"title": "Schema referenced from a parent",
"description": "Example description",
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
It also happens if the $ref
only appears once in the file but it’s inside an object in the definitions section:
samples/parent2.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://schemas.example.com/samples/parent2",
"title": "Schema that references another schema",
"description": "Example description",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"children": {
"type": "array",
"items": {
"$ref": "#/definitions/child"
}
}
},
"definitions": {
"child": {
"type": "object",
"properties": {
"order": {
"type": "number"
},
"details": {
"$ref": "./child.schema.json"
}
}
}
}
}
This seems to be because traverse()
finds it multiple times.
Your code
const Ajv = require('ajv').default
const $RefParser = require('json-schema-ref-parser')
const ajv = new Ajv();
$RefParser.dereference("samples/parent.schema.json", function(err, schema) {
if (err) throw err
try {
ajv.compile(schema);
console.log('[ PASS ] Schema validation passed.')
}
catch (e) {
console.log(`[ FAIL ] Schema validation failed: "${e.message}"]`)
process.exit(1)
}
})
Validation result, data AFTER validation, error messages
[ FAIL ] Schema validation failed: "reference "https://schemas.example.com/samples/child" resolves to more than one schema"]
What results did you expect? Schemas pass compilation. It’s perfectly valid to reference an external schema multiple times or from inside local definitions, and it used to work fine.
Are you going to resolve the issue? Maybe, but I’m not quite sure where to start. It seems like it’s not necessary to throw an error if a schema is referenced more than once, since it didn’t used to. But I assume that code was added for a reason, and I don’t know what that reason is, or if anything bad would happen if it were removed.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:3
- Comments:5 (2 by maintainers)
The problem here is that after you use json-schema-ref-parser, you replace two identical refs with two different schema objects, that have the same $id (which is exactly what the error says).
The difference with Ajv v6 is that v7 changed from comparing serialized schemas (and they had to be stable-stringified in v6, not just JSON.serialize). Because of that what v6 saw as identical schemas, v7 sees as different schemas (because it compares them by reference).
While this change does cause some confusion - see #1413 for example - it also highlights the cases when Ajv is used incorrectly (e.g. using ref resolution outside of Ajv as in your example - see below the working approach - or not reusing compiled schemas, and instead at best serialising the schema every time validation is needed as in #1413 or even worse - compiling them every time).
The motivation for this change was:
For your particular case, you do not need to use json-schema-ref-parser, instead you need to change
$ref
s in schemas to use schema$id
s (absolute, or relative, but not file paths) and add both schemas to Ajv - it should correctly do reference resolution:You could also use addSchema method to add them not via options:
Parent schema has to be changed in this way (using relatively URI in $ref):
This is working repl: https://runkit.com/esp/6020572c572324001a6a3e55
While Ajv does not inline the schema, it still, when possible, inlines the code - as in this case. This behaviour can be changed using the option inlineRefs.
If for some other usage you need a resolved schema you can still use json-schema-ref-parser, as Ajv does not produce it (in general case, it is not possible, because of recursion)
If for some other reason you do need a resolved schema AND it has to be the same schema that you pass to Ajv (although I cannot think why it may be required) then you need to preprocess the schema to make sure all
$id
s are unique (or they can be simply removed from the inlined fragments - they are not needed there.Not sure what happened with the parent2 example, when I inline clild inside parent2 it compiles: https://runkit.com/esp/6020565602c3d0001aa8e771. It is possible that you have both parent and parent2 compiled by the same Ajv instance in which case child IDs would conflict for the same reason.
We are also getting this issue, and struggling to find any answers…