Skip to content

feat(library): resolve JSON Schema dialects when reading - #3160

Draft
Haseeb Nazir (iamhaseebn) wants to merge 5 commits into
microsoft:mainfrom
iamhaseebn:feat/json-schema-dialect-support
Draft

Haseeb Nazir (iamhaseebn) wants to merge 5 commits into
microsoft:mainfrom
iamhaseebn:feat/json-schema-dialect-support

Conversation

@iamhaseebn

@iamhaseebn Haseeb Nazir (iamhaseebn) commented Nov 3, 2026 •

Copy link
Copy Markdown

Pull Request

Description

This starts the reader-side work for #2963. Schemas use their explicit $schema, then the enclosing schema's dialect, the document's jsonSchemaDialect, and finally the OpenAPI default.

The known dialects use the existing version services. Unknown dialects keep their schema data and produce a diagnostic. With external loading enabled, their meta-schema chains can resolve to a supported dialect without parsing the document again, so custom reader changes and callbacks are preserved.

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
  • Other (please describe):

Related Issue(s)

Related to #2963. This is a draft for the reader work discussed there, not a claim that the full issue is complete.

Changes Made

  • Add context-owned dialect selection for OpenAPI 3.1 and 3.2, using the existing schema readers.
  • Resolve unknown meta-schema chains through the configured loader, with per-read caching, cycle detection, cancellation, a 16-load limit and a 1 MiB response limit.
  • Correct absolute-URI handling in the default loader and add reader, loader, diagnostic and callback regressions, plus usage documentation.

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • All existing tests pass

Verified locally for PR head 24af207, based on main 113803c, using .NET SDK 10.0.403:

  • Dialect-focused tests: 88 passed.
  • Full solution tests with coverage: 2,041 passed, no failures or skips.
  • Release solution build: no warnings or errors.

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Versions applicability

  • My change applies to the version 1.X of the library, if so PR link:
  • My change applies to the version 2.X of the library, if so PR link: not opened yet.
  • My change applies to the version 3.X of the library, if so PR link: this PR.
  • I have evaluated the applicability of my change against the other versions above.

The 3.1 reader behavior and loader correction are also relevant to 2.X; no backport is included in this draft. Version 1.X has no 3.1/3.2 dialect support and its loader does not use the affected synthetic-base path.

See the contributing guidelines for how patches are applied across versions.

Additional Notes

Writing still follows the requested OpenAPI output version. This does not add parsing rules for older JSON Schema drafts or custom vocabularies; it resolves chains to the two dialects already handled by the version services. The writer-side direction can be worked through during draft review.

The repository's performance and Windows trimming/NativeAOT checks were not run locally and still need to pass before merge.

Signed-off-by: Haseeb Nazir <36381672+iamhaseebn@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 3, 2026 11:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The fallback can discard unsupported schema constraints, while resolver timeout handling can abort document loading and fragment-insensitive mappings can incorrectly suppress unresolved-dialect diagnostics.

3 open issues
What changed in this PR

Adds reader-side JSON Schema dialect selection for OpenAPI 3.1 and 3.2, advancing #2963 while leaving serialization tied to the requested OpenAPI version.

Changes:

  • Selects dialects using schema declarations, inheritance, document settings, and defaults.
  • Adds opt-in meta-schema chain resolution with caching, cycle detection, and resource limits.
  • Corrects absolute-URI loading and adds documentation and regression coverage.
File Description
test/​Microsoft.OpenApi.Readers.Tests/​V32Tests/​OpenApiDocumentTests.cs Tests 3.2 dialect precedence and inheritance.
test/​Microsoft.OpenApi.Readers.Tests/​V31Tests/​OpenApiDocumentTests.cs Tests 3.1 dialect precedence and inheritance.
test/​Microsoft.OpenApi.Readers.Tests/​OpenApiReaderTests/​JsonSchemaDialectTests.cs Covers resolution, caching, cycles, and limits.
test/​Microsoft.OpenApi.Readers.Tests/​OpenApiReaderTests/​JsonSchemaDialectLoaderTests.cs Covers loading, cancellation, and cache isolation.
test/​Microsoft.OpenApi.Readers.Tests/​OpenApiReaderTests/​JsonSchemaDialectDiagnosticTests.cs Tests diagnostic, reader customization, and callback preservation.
src/​Microsoft.OpenApi/​Reader/​V32/​OpenApiV32VersionService.cs Exposes core 3.2 schema parsing.
src/​Microsoft.OpenApi/​Reader/​V32/​OpenApiSchemaDeserializer.cs Routes 3.2 schemas through dialect selection.
src/​Microsoft.OpenApi/​Reader/​V31/​OpenApiV31VersionService.cs Exposes core 3.1 schema parsing.
src/​Microsoft.OpenApi/​Reader/​V31/​OpenApiSchemaDeserializer.cs Routes 3.1 schemas through dialect selection.
src/​Microsoft.OpenApi/​Reader/​Services/​JsonSchemaDialectService.cs Tracks dialect scopes and unsupported-dialect diagnostics.
src/​Microsoft.OpenApi/​Reader/​Services/​JsonSchemaDialectResolver.cs Resolves external meta-schema chains.
src/​Microsoft.OpenApi/​Reader/​Services/​DefaultStreamLoader.cs Preserves absolute request URIs.
src/​Microsoft.OpenApi/​Reader/​ReadResult.cs Carries pending dialect-resolution metadata.
src/​Microsoft.OpenApi/​Reader/​ParsingContext.cs Initializes context-owned dialect selection.
src/​Microsoft.OpenApi/​Reader/​OpenApiModelFactory.cs Resolves dialects without reparsing documents.
src/​Microsoft.OpenApi/​Reader/​OpenApiJsonReader.cs Captures dialect metadata for asynchronous resolution.
src/​Microsoft.OpenApi/​Reader/​BaseOpenApiVersionService.cs Provides a schema-loading entry point.
src/​Microsoft.OpenApi/​Interfaces/​IOpenApiVersionService.cs Defines the schema-loading contract.
README.md Documents dialect behavior, configuration, and limits.

Review effort: Balanced


if (!_versionServices.TryGetValue(GetDialectKey(effectiveDialect), out var versionService))
{
versionService = _fallbackVersionService;
Signed-off-by: Haseeb Nazir <36381672+iamhaseebn@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 3, 2026 12:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Dialect URI fragments are ignored, potentially suppressing unsupported-dialect diagnostics, and non-boolean $vocabulary values in unsupported schemas can throw instead of being preserved with diagnostics.

1 open issue
2 previously missed issues

In code that hasn't changed since last review

Medium severity Recover vocabulary conversion failures in object values

src/​Microsoft.OpenApi/​Reader/​JsonNodeHelper.cs:301

An unsupported draft-07 schema with $vocabulary: { "urn:example:vocab": "custom" } throws FormatException through CreateSimpleMap(LoadBool) instead of preserving the keyword and returning a schema with diagnostics. The scalar-only recovery guard excludes this field because its value is a JsonObject. Recover conversion failures from non-schema containers too, while preserving exception propagation from explicitly supported nested schemas.

Medium severity Resolve dialect fragments before checking schema support

src/​Microsoft.OpenApi/​Reader/​Services/​JsonSchemaDialectResolver.cs:137

The default HTTP/file loader returns the whole resource, but this code reads only its root $schema, ignoring a non-empty dialect URI fragment. Consequently, https://example.com/meta#missing is treated as supported when the root declares a known dialect, and its unsupported-dialect diagnostic is removed. Resolve the JSON Pointer or anchor and determine the target schema's effective dialect before following the chain; retain the diagnostic if the target cannot be resolved.

2 resolved since last review

Review effort: Balanced

Signed-off-by: Haseeb Nazir <36381672+iamhaseebn@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 3, 2026 16:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Unsupported-dialect keyword extraction can throw and discard valid sibling schemas, while cached dialect resolution produces order-dependent diagnostics.

1 open issue
2 previously missed issues

In code that hasn't changed since last review

Medium severity Apply consistent depth limits to cached reference chains

src/​Microsoft.OpenApi/​Reader/​Services/​JsonSchemaDialectResolver.cs:74

For A0 -> A1 -> ... -> A16 -> known, resolving A0 before A1 leaves A0 unsupported, but resolving A1 first caches its success and lets A0 succeed here. Both orders perform 17 loads overall, yet produce different diagnostics. Apply consistent limit accounting to cached chains (for example, retain their hop counts for a chain-depth limit), and add a regression that reverses schema order.

Medium severity Preserve incompatible reference keywords during initial schema extraction

src/​Microsoft.OpenApi/​Reader/​Services/​JsonSchemaDialectService.cs:72

For an unsupported dialect, an object-valued $dynamicRef throws during LoadSchemaCore's initial keyword extraction, before fallback preservation runs. In components.schemas, this aborts the map assignment and loses otherwise valid sibling schemas too. Make the initial $ref, $dynamicRef, and $id extraction fallback-aware in both V31 and V32 deserializers, preserving incompatible values as raw keywords while continuing to parse the schema.

Review effort: Balanced

Signed-off-by: Haseeb Nazir <36381672+iamhaseebn@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 3, 2026 17:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Human assessment is needed because diagnostics for unsupported schema declarations in composition arrays omit element indices, causing distinct declarations to share the same reported location.

1 open issue

Review effort: Balanced

Signed-off-by: Haseeb Nazir <36381672+iamhaseebn@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 3, 2026 19:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Security-sensitive external meta-schema loading and inferred dialect compatibility require maintainer validation of trust boundaries, with Windows trimming/NativeAOT checks still outstanding.

1 open issue

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants