Skip to content

Feature: Resolve $dynamicRef against $dynamicAnchor in the schema reference resolution pipeline #2911

Description

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:

  1. Treat $dynamicRef similarly to $ref during schema loading, producing a schema reference type whose target resolution uses $dynamicAnchor.
  2. 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.

AspectDocument-scoped lookupDynamic-scope resolution
Simple recursion (single anchor)WorksWorks
Generic types (multiple anchors, same name)Fails — no contextWorks
Implementation sizeSmall (index + lookup)Large (scope plumbing + walker changes)
Risk to existing Target semanticsLow (additive)Medium-high (new resolution path)
Precedent in repo$id registration patternLoopDetector, ResolveRecursiveTarget
Order-dependencyNoneInherent (documented in kiota)
Solves the pure-$dynamicRef blocker?Needs deserializer changeNeeds deserializer change

Related

Metadata

Metadata

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions