$dynamicRef : URI Reference

$dynamicRef

URI Reference

This 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!

Examples

After leaving a dynamic scope, '$dynamicAnchor' is not used by a '$dynamicRef' Schema
{
  "$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"
    }
  }
}
Invalid String matches '/$defs/thingy', but the '$dynamicRef' does not stop here Instance
"a string"
Invalid firstScope is not in dynamic scope for the '$dynamicRef' Instance
42
Valid '/then/$defs/thingy' is the final stop for the '$dynamicRef' Instance
null
  • The evaluation begins with the top-level schema, where the dynamic scope is the root schema resource.

    • Dynamic Scope: https://example.com/root
  • 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
  • Since https://example.com/firstScope doesn’t reference any other schema resource, the evaluation of the if schema completes, and the stack unwinds, returning to the root schema resource.

    • Dynamic Scope: https://example.com/root
  • The successful validation by the if subschema triggers entry into the then applicator, introducing another schema resource (https://example.com/secondScope), thus extending the dynamic scope.

    • Dynamic Scope: https://example.com/root –> https://example.com/secondScope
  • 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
  • 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 with '$dynamicRef' and '$dynamicAnchor' keywords in the same schema resource Schema
{
  "$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
    }
  }
}
Valid An instance adhering to the above schema is valid Instance
{
  "name": "John",
  "age": 35,
  "address": "1234 Elm Street, Springfield, IL 62701, USA"
}
Invalid Required properties must be present Instance
{ "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 with multiple '$dynamicAnchor' set to same value Schema
{
  "$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"
        }
      }
    }
  }
}
Valid An instance adhering to the above schema is valid Instance
[ "foo", "bar" ]
Invalid A non-string array is invalid Instance
[ 11, 22 ]
  • A $dynamicRef resolves to the first $dynamicAnchor still in scope that is encountered when the schema is evaluated.