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: categorytype: objectproperties:
children:
type: arrayitems:
$dynamicRef: '#category'LocalizedCategory:
$dynamicAnchor: categoryallOf:
- $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