$dynamicRef : URI Reference
$dynamicRef
URI ReferenceThis keyword is used to reference an identified schema, deferring the full resolution until runtime, at which point it is resolved each time it is encountered while evaluating an instance.
Value | This keyword must be set to an absolute URI or a relative reference as defined by RFC 3986, where its fragment (if any) can consist of a JSON Pointer as defined by RFC 6901 |
---|---|
Kind | Applicator |
Applies To | Any |
Dialect | 2020-12 |
Changed In | None |
Introduced In | 2020-12 |
Vocabulary | Core |
Specification | https://json-schema.org/draft/2020-12/json-schema-core.html#section-8.2.3.1 |
Metaschema | https://json-schema.org/draft/2020-12/meta/core |
Official Tests | draft2020-12/dynamicRef.json |
Default | None |
Annotation | None |
Affected By | |
Affects | None |
Also See |
The $dynamicRef
keyword is a dynamic applicator that allows for runtime resolution of schema references. Unlike the static $ref
, which resolves the referenced schema at schema load time, $dynamicRef
defers full resolution until the instance is evaluated. It attempts to resolve the given fragment based on the dynamic scope at that given point in time.
- This keyword is particularly useful for handling recursive schemas where the schema references itself or where the structure of the schema may change at runtime.
- If the
$dynamicRef
includes more than just a fragment, the URI except for the fragment is statically resolved first, and then only the fragment is dynamically resolved. - Notably, official meta-schemas use this mechanism themselves for defining vocabularies!
Digging Deeper
URIs play a central role in JSON Schema. Going through the URI RFC 3986 specification is a must for gaining a deeper understanding of references, identifiers, and anchors. More specifically, we recommend carefully studying URI resolution, URLs vs URNs, and the difference between a URI and a URI Reference.
You may also find these blog posts helpful for gaining a deeper understanding of dynamic references.
Common Pitfall
Bookending: The bookending requirement means that when you use a
$dynamicRef
, the JSON Schema processor needs to find a matching $dynamicAnchor
within the target schema resource, even if the target is different from the destination schema. This ensures that the reference resolves correctly by matching the actual anchor defined in the target schema resource, preventing unresolvable references due to scope issues.
Examples
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/root",
"if": {
"$id": "firstScope",
"$defs": {
"thingy": {
"$comment": "this is firstScope#thingy",
"$dynamicAnchor": "thingy",
"type": "number"
}
}
},
"then": {
"$id": "secondScope",
"$ref": "start",
"$defs": {
"thingy": {
"$comment": "this is secondScope#thingy, the final destination of the $dynamicRef",
"$dynamicAnchor": "thingy",
"type": "null"
}
}
},
"$defs": {
"start": {
"$comment": "this is the landing spot from $ref",
"$id": "start",
"$dynamicRef": "innerScope#thingy"
},
"thingy": {
"$comment": "this is the first stop for the $dynamicRef",
"$id": "innerScope",
"$dynamicAnchor": "thingy",
"type": "string"
}
}
}
"a string"
42
null
-
The evaluation begins with the top-level schema, where the dynamic scope is the root schema resource.
- Dynamic Scope:
https://example.com/root
- Dynamic Scope:
-
Upon encountering the
if
applicator, a new schema resource (https://example.com/firstScope
) is declared and added to the stack, expanding the dynamic scope.- Dynamic Scope:
https://example.com/root
–>https://example.com/firstScope
- Dynamic Scope:
-
Since
https://example.com/firstScope
doesn’t reference any other schema resource, the evaluation of theif
schema completes, and the stack unwinds, returning to the root schema resource.- Dynamic Scope:
https://example.com/root
- Dynamic Scope:
-
The successful validation by the
if
subschema triggers entry into thethen
applicator, introducing another schema resource (https://example.com/secondScope
), thus extending the dynamic scope.- Dynamic Scope:
https://example.com/root
–>https://example.com/secondScope
- Dynamic Scope:
-
Within the
then
subschema, a reference to another schema resource (https://example.com/start
) further enriches the dynamic scope.- Dynamic Scope:
https://example.com/root
–>https://example.com/secondScope
–>https://example.com/start
- Dynamic Scope:
-
Additionally, within the
then
subschema, a dynamic reference is made to another schema resource (https://example.com/innerScope#thingy
). While the initial part of the URI is resolved statically to/$defs/thingy
, the inclusion of#thingy
fragment reults in the final resolution to/then/$defs/thingy
because the first dynamic anchor encountered in the current dynamic scope is at/then/$defs/thingy
. So, only a null value is valid in this case.
Note: The non-fragment part is always statically resolved, while the fragment may be dynamically resolved.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [ "name", "age", "address" ],
"properties": {
"name": { "$dynamicRef": "#name" },
"age": { "$dynamicRef": "#age" },
"address": { "$ref": "#address" }
},
"$defs": {
"name": {
"$dynamicAnchor": "name",
"type": "string",
"minLength": 3
},
"age": {
"$anchor": "age",
"type": "integer"
},
"address": {
"$dynamicAnchor": "address",
"type": "string",
"maxLength": 50
}
}
}
{
"name": "John",
"age": 35,
"address": "1234 Elm Street, Springfield, IL 62701, USA"
}
{ "name": "Doe", "age": 61 }
- A
$dynamicRef
referencing a$dynamicAnchor
within the same schema resource functions similarly to a standard$ref
referencing an$anchor
. Similarly, a$dynamicRef
referencing an$anchor
within the same schema resource behaves like a typical$ref
referencing an$anchor
. Likewise, a$ref
targeting a$dynamicAnchor
within the same schema resource behaves like a regular$ref
targeting an$anchor
.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/root",
"$ref": "list",
"$defs": {
"foo": {
"$dynamicAnchor": "items",
"type": "string"
},
"list": {
"$id": "list",
"items": { "$dynamicRef": "#items" },
"$defs": {
"items": {
"$dynamicAnchor": "items",
"type": "integer"
}
}
}
}
}
[ "foo", "bar" ]
[ 11, 22 ]
- A
$dynamicRef
resolves to the first$dynamicAnchor
still in scope that is encountered when the schema is evaluated.