json-schema-spec
json-schema-spec copied to clipboard
Referencing schemas introduced by applicator keywords
I have a question about using subschemas introduced by applicator keywords as targets in $ref and $dynamicRef.
In the spec, I see only two ways how schemas are introduced for referencing:
$defs- external schemas
The spec says that even unknown keyword values could be referenced as schemas, so implementations definitely MAY implement referencing of applicator keyword values. But should this feature be mandatory, or can it be considered optional?
Thanks!
You can reference any subschema in a $ref - it doesn't have to be in a $defs.
@karenetheridge thanks!
Does it mean that validation can be done only in two passes through the schema?
- During the first pass, we collect all subschemas, resolve their IDs, and resolve references to external schemas
- During the second pass, we validate the instance using information from the first pass.
Example:
{
"$id": "root",
"$ref": "inner",
"items": {
"$id": "inner"
}
}
Here we can resolve reference inner only if we have already processed the items keyword schema.
Do I understand it correctly?
You don't need to resolve references in the first pass, and sometimes you can't, because the resolution is dynamic, depending on the runtime evaluation path.
It's also a good idea to verify that the structure is correct in this first pass -- because otherwise you can't do anything else with the schema later.
You do need to find and fully-resolve identifiers ($id, $anchor, $recursiveAnchor, $dynamicAnchor keywords) so at runtime you are able to properly handle $refs to them -- as the identifier may be in a location that you haven't evaluated yet (and technically evaluation of keywords can happen in any order, with only a few restrictions, namely some keywords in the Core and Unevaluated vocabularies).
You don't need to resolve references in the first pass, and sometimes you can't, because the resolution is dynamic, depending on the runtime evaluation path.
Yes, I wrote it incorrectly. I meant that we need to resolve reference URIs against URI base during the first pass (for $dynamicRef we can resolve only starting point URI, of course).
So if I got everything correctly, two passes are required.
@karenetheridge thank you for the detailed answer!
Should I close this issue?
Yes, I wrote it incorrectly. I meant that we need to resolve reference URIs against URI base during the first pass
But we don't, though. That can be deferred to runtime with no ill effects.
Technically yes, but in this case, we can face the situation when reference points to the external schema, which wasn't processed during the first pass. Implementation must be ready to handle such a situation.
@yakimun it is necessary to, at some point, scan each schema document for embedded resources ($id) and plain name fragments ($anchor and (in 2020-12) $dynamicAnchor). This can be done at load time (see discussion #220 for the other parts of that presentation), which is what I would recommend. Or it can be done by going back and scanning known schema documents when you hit an unknown URI in a reference (which seems complicated to me but I guess some people do it).
Whether you pre-resolve references using the appropriate base URIs or keep track of the current base URI during evaluation and resolve only when you use the reference doesn't matter in terms of behavior, which I think is what @karenetheridge was talking about deferring to runtime. Although technically you can defer the scanning as well as I noted, it just gets complicated.
I think this has been thoroughly answered (with no further comments in over half a year) and there's no action to take for the spec, so I'm going to close this.