Background
JSON Schema 2020-12 defines $dynamicRef / $dynamicAnchor for recursive and extensible schema resources. This enables patterns where a base schema can recursively refer to the active derived schema:
components:
schemas:
BaseCategory:
$dynamicAnchor: category
type: object
properties:
children:
type: array
items:
$dynamicRef: '#category'
LocalizedCategory:
$dynamicAnchor: category
allOf:
- $ref: '#/components/schemas/BaseCategory'
When LocalizedCategory is the active schema, children.items should resolve to LocalizedCategory, not BaseCategory.
Current State
After #2896, JSON Schema 2020-12 keywords such as $dynamicRef, $dynamicAnchor, and $defs are parsed, preserved, and serialized, including as siblings on $ref schemas.
However, $dynamicRef is not currently part of schema reference resolution:
BaseOpenApiReferenceHolder.Target resolves $ref through OpenApiDocument.ResolveReference.
OpenApiWorkspace indexes schemas by component path and $id, but not by $dynamicAnchor.
JsonNodeHelper.GetReferencePointer only checks $ref.
- A schema with
$dynamicRef but no $ref is deserialized as a plain OpenApiSchema with DynamicRef set, so it does not enter the OpenApiSchemaReference.Target resolution path.
Design Questions
There are two separate decisions to make.
1. How should bare $dynamicRef schemas participate in resolution?
Options:
- Treat
$dynamicRef similarly to $ref during schema loading, producing a schema reference type whose target resolution uses $dynamicAnchor.
- Keep
$dynamicRef as metadata on OpenApiSchema, but add a separate dynamic-ref resolution path outside Target.
Option 1 integrates with the existing reference model but requires Target to resolve context-dependently. Option 2 keeps $dynamicRef resolution explicit and doesn't conflate it with static $ref resolution.
2. What scope should resolution support?
A document-level $dynamicAnchor lookup handles the simple case where only one schema declares an anchor.
Full JSON Schema 2020-12 behavior requires dynamic-scope resolution: when multiple resources declare the same $dynamicAnchor, the outermost active schema resource wins. That requires context that the current Target / Workspace.ResolveReference pipeline does not carry today.
| Aspect |
Document-scoped lookup |
Dynamic-scope resolution |
| Simple recursion (single anchor) |
Works |
Works |
| Generic types (multiple anchors, same name) |
Fails — no context |
Works |
| Implementation size |
Small (index + lookup) |
Large (scope plumbing + walker changes) |
Risk to existing Target semantics |
Low (additive) |
Medium-high (new resolution path) |
| Precedent in repo |
$id registration pattern |
LoopDetector, ResolveRecursiveTarget |
| Order-dependency |
None |
Inherent (documented in kiota) |
Solves the pure-$dynamicRef blocker? |
Needs deserializer change |
Needs deserializer change |
Related
Background
JSON Schema 2020-12 defines
$dynamicRef/$dynamicAnchorfor recursive and extensible schema resources. This enables patterns where a base schema can recursively refer to the active derived schema:When
LocalizedCategoryis the active schema,children.itemsshould resolve toLocalizedCategory, notBaseCategory.Current State
After #2896, JSON Schema 2020-12 keywords such as
$dynamicRef,$dynamicAnchor, and$defsare parsed, preserved, and serialized, including as siblings on$refschemas.However,
$dynamicRefis not currently part of schema reference resolution:BaseOpenApiReferenceHolder.Targetresolves$refthroughOpenApiDocument.ResolveReference.OpenApiWorkspaceindexes schemas by component path and$id, but not by$dynamicAnchor.JsonNodeHelper.GetReferencePointeronly checks$ref.$dynamicRefbut no$refis deserialized as a plainOpenApiSchemawithDynamicRefset, so it does not enter theOpenApiSchemaReference.Targetresolution path.Design Questions
There are two separate decisions to make.
1. How should bare
$dynamicRefschemas participate in resolution?Options:
$dynamicRefsimilarly to$refduring schema loading, producing a schema reference type whose target resolution uses$dynamicAnchor.$dynamicRefas metadata onOpenApiSchema, but add a separate dynamic-ref resolution path outsideTarget.Option 1 integrates with the existing reference model but requires
Targetto resolve context-dependently. Option 2 keeps$dynamicRefresolution explicit and doesn't conflate it with static$refresolution.2. What scope should resolution support?
A document-level
$dynamicAnchorlookup handles the simple case where only one schema declares an anchor.Full JSON Schema 2020-12 behavior requires dynamic-scope resolution: when multiple resources declare the same
$dynamicAnchor, the outermost active schema resource wins. That requires context that the currentTarget/Workspace.ResolveReferencepipeline does not carry today.Targetsemantics$idregistration patternLoopDetector,ResolveRecursiveTarget$dynamicRefblocker?Related
$refsiblings ($defs,$dynamicAnchor,$id) are silently dropped when parsing #2895