Skip to content

feat: Support $recursiveRef in Draft 2019-09 - #984

Open
Roman3349 wants to merge 1 commit into
jsonrainbow:mainfrom
Roman3349:feat/recursive-ref
Open

Roman3349 wants to merge 1 commit into
jsonrainbow:mainfrom
Roman3349:feat/recursive-ref

Conversation

@Roman3349

@Roman3349 Roman3349 commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Draft 2019-09 ignored the $recursiveRef keyword, so values evaluated through it were never validated.

  • Loading: when a schema is loaded, $recursiveRef is expanded against the base URI of its own schema, the same way $ref is.
  • Dynamic scope: while validating, a new DynamicScope keeps track of the schema resources that have been entered: the root schema and every schema with an $id. Draft2019Constraint adds a resource when it enters one and removes it in finally. All nested Draft 2019-09 constraints share one scope through Draft2019\Factory.
  • Resolution: Factory::resolveRecursiveRef() first resolves the target like a $ref. If that target has "$recursiveAnchor": true, it walks the dynamic scope from the innermost resource outwards and keeps going while each resource also has "$recursiveAnchor": true. The last one reached becomes the target.
  • unevaluatedProperties: UnevaluatedPropertiesConstraint now follows $recursiveRef too when it collects the properties that have been evaluated.

Related Issue

None.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Other (please describe):

Checklist

  • I have read the CONTRIBUTING guidelines
  • My code follows the code style of this project
  • I have added tests that prove my fix is effective or that my feature works
  • All new and existing tests pass
  • I have updated the documentation accordingly
  • My changes generate no new warnings

Additional Notes

The $recursiveRef keyword was ignored, so values evaluated through it were
never validated.

The reference is expanded against the base uri of its schema when the
schema is loaded, like $ref. During the evaluation, the schema resources
entered (the root schema and each schema with an $id) are kept as the
dynamic scope, shared by all nested Draft 2019-09 constraints. When the
initial target declares "$recursiveAnchor": true, the outermost resource
of the dynamic scope reachable through resources that declare it as well
becomes the destination.

unevaluatedProperties follows $recursiveRef as well when collecting the
evaluated properties.

Signed-off-by: Roman Ondráček <mail@romanondracek.cz>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants