Skip to content

Conversation

@reuvenharrison
Copy link
Contributor

@reuvenharrison reuvenharrison commented Feb 9, 2026

Summary

Comprehensive OpenAPI 3.1 / JSON Schema 2020-12 support. This consolidates and supersedes PRs #1102, #1114, #1118#1124.

Core features

  • Type arrays with null support (e.g., ["string", "null"])
  • JSON Schema 2020-12 keywords: const, examples, prefixItems, contains, minContains, maxContains, patternProperties, dependentSchemas, propertyNames, unevaluatedItems, unevaluatedProperties
  • Conditional keywords: if/then/else, dependentRequired
  • Identity/referencing keywords: $id, $anchor, $dynamicRef, $dynamicAnchor
  • Content vocabulary: contentMediaType, contentEncoding, contentSchema
  • Core keywords: $schema, $comment, $defs
  • ExclusiveBound union type: supports both boolean (3.0) and numeric (3.1) exclusive bounds
  • Webhooks with $ref resolution
  • Version detection: IsOpenAPI3_0(), IsOpenAPI3_1()
  • Info.Summary and License.Identifier fields

Validation

  • JSON Schema 2020-12 validator via EnableJSONSchema2020() (per-field) and EnableJSONSchema2020Validation() (document-level)
  • Auto-detection: doc.Validate() automatically enables JSON Schema 2020-12 mode for OpenAPI 3.1 documents
  • Built-in validator: const keyword enforcement, IsEmpty() awareness of all new fields
  • $ref resolution in loader for all new schema fields (including $defs)
  • Schema.validate() recursion into all new sub-schema fields
  • transformOpenAPIToJSONSchema recursion into all nested schemas (including $defs)
  • Allow $ref alongside other keywords (legal in 3.1)
  • Handle "null" type in schema validation

Backward compatibility

  • 100% backward compatible with OpenAPI 3.0
  • ExclusiveBound transparently handles both boolean and numeric forms
  • openapi2conv updated to convert between ExclusiveBound and boolean, preserving numeric bound values

Commit history

  1. feat: add OpenAPI 3.1 support — base implementation (Types, Schema fields, Webhooks, JSON Schema validator, version helpers)
  2. fix: resolve $ref in OpenAPI 3.1 schema fields — loader handles refs in prefixItems, contains, patternProperties, etc.
  3. fix: recurse into OpenAPI 3.1 fields in transformOpenAPIToJSONSchema — transform handles all nested schemas
  4. fix: validate sub-schemas in OpenAPI 3.1 schema fields — Schema.validate() recurses into new fields
  5. feat: add const keyword validation to built-in validator — const enforcement without EnableJSONSchema2020()
  6. fix: improve OpenAPI 3.1 spec compliance — paths optional in 3.1, mutualTLS, license url/identifier exclusivity, auto-enable JSON Schema 2020-12 in openapi3filter
  7. feat: add remaining JSON Schema 2020-12 keywords — $id, $anchor, $dynamicRef, $dynamicAnchor, contentMediaType, contentEncoding, contentSchema, discriminator for anyOf
  8. style: fix go fmt formatting
  9. fix: resolve 8 correctness issues — IsEmpty(), JSONLookup(), validate() array items, transform bugs, MarshalYAML, const comparison, webhooks ordering
  10. fix: resolve 8 additional issues — $comment keyword, PrefixItems type consistency, exclusiveBoundToBool data loss, auto-enable 3.1 validation, test coverage gaps, README breaking changes
  11. feat: add $schema, $defs keywords and fix remaining issues — per-schema dialect, local schema definitions, jsonSchemaDialect URI validation, discriminator refactor

Open issues for complete 3.1 support

The following items are known gaps that can be addressed in follow-up PRs:

Missing features

  • $schema keyword — Added per-schema dialect declaration.
  • $defs keyword — Added with full support (struct, marshal, unmarshal, IsEmpty, JSONLookup, validate, loader, transform).
  • $comment keyword — Added as first-class field.
  • $dynamicRef / $dynamicAnchor not resolved — Parsed and serialized but the loader does not resolve them. Schemas using $dynamicRef for recursive references will not work.
  • Built-in validator ignores most 3.1 keywords — Only validates const. Other 3.1 keywords are silently ignored without EnableJSONSchema2020().
  • contentMediaType / contentEncoding not validated at runtime — Spec-compliant (annotation-only in 2020-12), but some users may expect validation.
  • Missing pathItems in Components struct — OAS 3.1 added components/pathItems. Completely absent — silently dropped during parsing.

Bugs / correctness

  • Schema.JSONLookup() missing all new 3.1 fields — All fields added.
  • IsEmpty() missing checks for most new 3.1 fields — All fields added.
  • validate() requires items for type array even in 3.1 — Relaxed.
  • transformOpenAPIToJSONSchema does not clean up exclusiveMinimum: false — Fixed.
  • transformOpenAPIToJSONSchema drops nullable: true without type — Fixed.
  • MarshalYAML unconditionally emits "paths": null for 3.1 docs — Fixed.
  • visitConstOperation uses == for json.Number — Fixed.
  • Webhooks validation iterates non-deterministically — Fixed.
  • exclusiveBoundToBool loses constraint data — Fixed.
  • doc.Validate() does not auto-enable 3.1 mode — Fixed.
  • jsonSchemaDialect URI validation is a no-op — Now requires scheme.
  • Built-in validator applies items to ALL array elements, ignoring prefixItems — In 3.1, items applies only beyond the prefixItems tuple.
  • patternProperties omission changes additionalProperties semanticsadditionalProperties: false incorrectly rejects properties matching pattern properties.
  • Silent fallback in visitJSONWithJSONSchema — Silently falls back to built-in validator when JSON Schema compilation fails.
  • Const: nil cannot express "value must be null" — Go nil is the zero value for any.

Breaking API changes

  • ExclusiveMin/ExclusiveMax type changed — Documented in README.
  • PrefixItems type changed — Documented in README.

Code quality

  • PrefixItems type inconsistency — Changed to SchemaRefs.
  • Discriminator logic duplicated for anyOf — Refactored into resolveDiscriminatorRef helper.

Test coverage

  • No ref resolution test for ContentSchema — Added.
  • No ref resolution test for If/Then/Else — Added.
  • No transformOpenAPIToJSONSchema test for ContentSchema — Added.
  • No Schema.validate() test for ContentSchema — Added.

Test plan

  • Type arrays: serialization, deserialization, helper methods, backward compatibility
  • Webhooks: marshal/unmarshal, validation, JSONLookup
  • Version detection: IsOpenAPI3_0, IsOpenAPI3_1, migration scenarios
  • JSON Schema 2020-12 validator: type, number, object, array, oneOf/anyOf/allOf/not, exclusive bounds, const, nullable
  • if/then/else: conditional validation, marshal round-trip, unresolved ref detection
  • dependentRequired: conditional property requirements
  • Loader: $ref resolution in all 3.1 schema fields + if/then/else + contentSchema
  • Schema.validate(): unresolved ref detection in all new sub-schema fields + contentSchema
  • Const: string, number, boolean, null, object, type+const, multiError
  • Full test suite passes (go test ./...)

Supersedes

Closes #1102, closes #1114, closes #1118, closes #1119, closes #1120, closes #1121, closes #1122, closes #1123, closes #1124

🤖 Generated with Claude Code

Co-Authored-By: Chance Kirsch <>
Co-Authored-By: RobbertDM <>

reuvenharrison and others added 5 commits February 9, 2026 11:46
Add comprehensive OpenAPI 3.1 / JSON Schema 2020-12 support:

- Type arrays with null support (e.g., ["string", "null"])
- JSON Schema 2020-12 keywords: const, examples, prefixItems, contains,
  minContains, maxContains, patternProperties, dependentSchemas,
  propertyNames, unevaluatedItems, unevaluatedProperties
- Conditional keywords: if/then/else, dependentRequired
- ExclusiveBound union type for 3.0 (boolean) and 3.1 (numeric) exclusive bounds
- Webhooks support with $ref resolution
- Version detection helpers: IsOpenAPI3_0(), IsOpenAPI3_1()
- Info.Summary and License.Identifier fields
- JSON Schema 2020-12 validator via EnableJSONSchema2020()
- Document-level validation option: EnableJSONSchema2020Validation()
- Allow $ref alongside other keywords in 3.1 schemas
- Handle "null" type in schema validation
- Auto-detect 3.1 in cmd/validate

Co-Authored-By: Chance Kirsch <>
Co-Authored-By: RobbertDM <>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, etc.)

The loader's resolveSchemaRef only resolved $ref in pre-3.1 fields (items,
properties, additionalProperties, not, allOf, anyOf, oneOf). References
inside the new OpenAPI 3.1 / JSON Schema 2020-12 fields were silently
left unresolved, causing nil Value pointers.

This adds ref resolution for: prefixItems, contains, patternProperties,
dependentSchemas, propertyNames, unevaluatedItems, unevaluatedProperties.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The OpenAPI-to-JSON-Schema transformation only recursed into pre-3.1
fields (properties, additionalProperties, items, not, oneOf, anyOf,
allOf). Nested schemas inside 3.1 fields with OpenAPI 3.0-isms like
nullable:true were not converted, causing incorrect validation.

This adds recursion into: prefixItems, contains, patternProperties,
dependentSchemas, propertyNames, unevaluatedItems,
unevaluatedProperties.

Also consolidates the properties/patternProperties/dependentSchemas
map iteration into a single loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Schema.Validate() (used by doc.Validate()) recursively validates
sub-schemas in Items, Properties, AdditionalProperties, etc. but did
not recurse into the new OpenAPI 3.1 / JSON Schema 2020-12 fields.
Invalid sub-schemas nested inside these fields went undetected.

This adds validation for: prefixItems, contains, patternProperties,
dependentSchemas, propertyNames, unevaluatedItems,
unevaluatedProperties.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
reuvenharrison and others added 3 commits February 9, 2026 12:09
- Make paths optional in 3.1 (required only in 3.0)
- Add mutualTLS security scheme type validation
- Validate license url/identifier mutual exclusivity
- Enable JSON Schema 2020-12 validation in openapi3filter for 3.1 docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add $id, $anchor, $dynamicRef, $dynamicAnchor identity keywords
- Add contentMediaType, contentEncoding, contentSchema vocabulary
- Add discriminator support for anyOf (was only oneOf)
- Validate jsonSchemaDialect as valid URI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
reuvenharrison and others added 5 commits February 10, 2026 11:14
- IsEmpty(): add missing checks for PrefixItems, Contains, MinContains,
  MaxContains, PatternProperties, DependentSchemas, PropertyNames,
  UnevaluatedItems, UnevaluatedProperties, Examples
- JSONLookup(): add all 23 missing JSON Schema 2020-12 field cases
- validate(): relax items requirement for arrays when in 3.1 mode or
  when prefixItems is present
- transformOpenAPIToJSONSchema: clean up exclusiveMinimum/Maximum false,
  handle nullable:true without type field
- MarshalYAML: only emit paths when non-nil (valid in 3.1)
- visitConstOperation: use reflect.DeepEqual for json.Number comparison
- Webhooks validation: use componentNames() for deterministic ordering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rephrase text to not contain literal json struct tag syntax that
triggers the json/yaml tag consistency check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Open issues are tracked in the PR getkin#1125 description instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add $comment keyword to Schema struct (MarshalYAML, UnmarshalJSON,
  IsEmpty, JSONLookup)
- Fix PrefixItems type from []*SchemaRef to SchemaRefs for consistency
  with OneOf/AnyOf/AllOf and JSON Pointer support
- Fix exclusiveBoundToBool data loss: preserve numeric bound value
  when converting OAS 3.1 exclusive bounds to OAS 2.0
- Auto-enable JSON Schema 2020-12 validation for OpenAPI 3.1 documents
  in doc.Validate() so library users don't need explicit opt-in
- Add ref resolution tests for if/then/else and contentSchema
- Add transform test for contentSchema with nullable nested schema
- Add validate test for contentSchema with invalid sub-schema
- Document breaking API changes in README (ExclusiveBound, PrefixItems)
- Regenerate docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add $schema keyword to Schema struct for per-schema dialect declaration
- Add $defs keyword (Schemas map) for local reusable schema definitions,
  with full support: struct, marshal, unmarshal, IsEmpty, JSONLookup,
  validate (recurse), loader (resolve refs), transform (recurse)
- Fix jsonSchemaDialect URI validation to require a scheme
- Refactor discriminator resolution into shared helper to eliminate
  code duplication between oneOf and anyOf paths
- Regenerate docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@reuvenharrison
Copy link
Contributor Author

Open Issues Status Update

22 of 30 issues resolved across commits 9–11.

Resolved issues (22)

Bugs / correctness (11 fixed)

  • Schema.JSONLookup() — all 23 missing JSON Schema 2020-12 fields added
  • IsEmpty() — missing checks for PrefixItems, Contains, MinContains, MaxContains, PatternProperties, DependentSchemas, PropertyNames, UnevaluatedItems, UnevaluatedProperties, Examples
  • validate() array items — relaxed when jsonSchema2020ValidationEnabled or prefixItems present
  • transformOpenAPIToJSONSchema exclusiveMinimum:false — boolean false now cleaned up
  • transformOpenAPIToJSONSchema nullable without type — now sets type: ["null"]
  • MarshalYAML paths:null — only emits paths when non-nil
  • visitConstOperation json.Number — uses reflect.DeepEqual
  • ✅ Webhooks validation ordering — uses componentNames() for deterministic iteration
  • exclusiveBoundToBool data loss — numeric bound value preserved in OAS 2.0 conversion
  • doc.Validate() auto-enable 3.1 — auto-enables EnableJSONSchema2020Validation() for 3.1 docs
  • jsonSchemaDialect URI validation — now requires a scheme

Missing features (3 added)

  • $schema keyword — per-schema dialect declaration
  • $comment keyword — first-class field for round-trip fidelity
  • $defs keyword — local reusable schema definitions with full ref resolution

Code quality (2 fixed)

  • PrefixItems type — changed from []*SchemaRef to SchemaRefs
  • ✅ Discriminator duplication — refactored into resolveDiscriminatorRef helper

Breaking API changes (2 documented)

  • ExclusiveMin/ExclusiveMax type change — documented in README v0.132.0
  • PrefixItems type change — documented in README v0.132.0

Test coverage (4 added)

  • ✅ Ref resolution test for ContentSchema
  • ✅ Ref resolution test for If/Then/Else
  • transformOpenAPIToJSONSchema test for ContentSchema
  • Schema.validate() test for ContentSchema

Remaining open issues (8)

These are larger-scope items for follow-up PRs:

# Issue Category
1 $dynamicRef/$dynamicAnchor not resolved by loader Missing feature
2 Built-in validator ignores most 3.1 keywords (needs EnableJSONSchema2020()) Missing feature
3 pathItems missing from Components struct Missing feature
4 Built-in validator applies items to ALL elements, ignoring prefixItems Bug
5 patternProperties omission changes additionalProperties semantics Bug
6 Silent fallback in visitJSONWithJSONSchema when compilation fails Bug
7 Const: nil cannot express "value must be null" Bug
8 contentMediaType/contentEncoding not validated at runtime (spec-compliant) Missing feature

@reuvenharrison
Copy link
Contributor Author

Coverage vs Pierre Fenoll's Task List

Assessment of this PR against the tasks outlined by Pierre for complete OpenAPI 3.1 support:

Task Status Notes
De/serialization Complete All 3.1 fields with full round-trip (marshal/unmarshal/JSONLookup/IsEmpty)
Document validation Complete All fields validated, auto-detect 3.1, $ref resolution in loader
JSON Schema validation Mostly complete External lib (santhosh-tekuri/jsonschema/v6) chosen; 3.1 keywords delegated to it. Built-in validator only handles const.
3.0 ↔ 3.1 conversion Minimal Only ExclusiveBound conversion in openapi2conv. No general downgrade/upgrade path.
openapi3gen (reflection) Not started Zero changes to openapi3gen/
openapi3filter (middleware) Minimal Auto-enables JSON Schema 2020-12 mode for 3.1 specs in ValidateParameter, ValidateRequestBody, ValidateResponse
3.0/3.1 coexistence Complete IsOpenAPI3_0()/IsOpenAPI3_1(), ExclusiveBound union type, omitempty on all 3.1 fields
Tests Comprehensive 7 new test files, 2 testdata files

What this PR delivers (~60-70% of the full task list)

  • Full data model for all JSON Schema 2020-12 keywords (26+ new Schema fields)
  • Document-level features (Webhooks, JSONSchemaDialect, Info.Summary, License.Identifier, mutualTLS)
  • External JSON Schema 2020-12 validator with automatic 3.1 detection
  • 100% backward compatible with OpenAPI 3.0

Remaining items for follow-up PRs

  1. openapi3gen — Reflection-based generation of 3.1 schemas from Go types (largest gap)
  2. 3.0 ↔ 3.1 conversion — Downgrade path (prefixItems→items, type arrays→nullable, $defs→definitions) and upgrade path
  3. openapi3filter — Deeper middleware integration beyond auto-enabling 3.1 mode
  4. Built-in validator — Currently only validates const; other 3.1 keywords require EnableJSONSchema2020()
  5. $dynamicRef resolution — Parsed/serialized but loader does not resolve dynamic references
  6. components/pathItems — OAS 3.1 added this field; currently silently dropped during parsing

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.

1 participant