Problem
OpenAPI.NET currently preserves OpenAPI 3.1/3.2 $ref schema siblings on JsonSchemaReference and exposes them through OpenApiSchemaReference convenience getters using reference-first fallback behavior:
This behavior appears to be intentional. In #2903, @baywet wrote after discussions with @handrews and @darrelmiller that:
Any JSON Schema keyword can appear next to $ref ... some might be contradictory when appearing both in the reference and the referenced schema, in which case, it's up to the application to define what the behaviour should be.
And:
In the case where both are present, we have a precedent of returning the reference value for annotation keywords, so being consistent here makes sense. Then the application can check the reference and the target values, and compare them, if the difference is important to the application.
That explains why #2906 made the schema keyword getters reference-first across all keyword types.
The concern is that this convenience behavior can be easy to misread for JSON Schema assertion keywords.
In JSON Schema 2020-12, $ref siblings are evaluated additively. They are not overrides of the referenced schema.
Example:
components:
schemas:
Target:
type: object
minProperties: 5
Referencing:
$ref: '#/components/schemas/Target'
minProperties: 2
Both constraints apply. The sibling minProperties: 2 does not relax the target's minProperties: 5.
With the current object model:
schemaRef.MinProperties; // 2
schemaRef.Reference.MinProperties; // 2
schemaRef.Target.MinProperties; // 5
This can make assertion keywords appear to override target constraints, even though JSON Schema evaluation treats them compositionally.
Question
Should OpenApiSchemaReference getters continue to expose reference-first convenience behavior for all schema keywords, or should the library distinguish more explicitly between:
- authored sibling keywords on
JsonSchemaReference
- resolved target schema values via
Target
- an evaluation-oriented/composed schema view
Possible Directions
Option 1: Keep current behavior and document it
Keep Reference.X ?? Target?.X for all schema keyword getters.
Document that these getters are object-model convenience accessors, not a JSON Schema evaluation/effective-schema view. Consumers that care about conflicts or additive constraints should inspect both schemaRef.Reference and schemaRef.Target.
Option 2: Split annotation and assertion getter behavior
Keep reference-first getters for annotation-style keywords such as:
Description
Title
Default
Deprecated
ReadOnly
WriteOnly
Examples
Extensions
Return target values for assertion/structural keywords such as:
Type
Format
MinProperties
MaxProperties
Pattern
Required
Properties
AllOf
OneOf
AnyOf
Not
Items
If
Then
Else
This is more validation-oriented, but it breaks the uniform getter rule from #2903 and creates setter/getter asymmetry. For example, setting schemaRef.Type stores the sibling value on Reference, but reading schemaRef.Type would return the target value.
Option 3: Keep current getters and add an explicit evaluation/composition API later
Keep the current object-model behavior for compatibility and consistency.
Add a future API such as GetEffectiveSchema() or similar for consumers that need JSON Schema evaluation semantics. That API could preserve additive sibling assertions compositionally, for example by representing inlined references with allOf rather than flattening sibling assertions as overrides.
Related Concern: Inline Serialization
CopyReferenceAsTargetElementWithOverrides() currently creates an inline OpenApiSchema from the reference view. Because the reference view is reference-first, inline serialization can flatten target-plus-sibling state in a way that looks like sibling assertions override target assertions.
A semantically safer inline representation would preserve composition, for example:
allOf:
- <inlined target schema>
minProperties: 2
This may be better handled separately from the getter behavior.
Related
Problem
OpenAPI.NET currently preserves OpenAPI 3.1/3.2
$refschema siblings onJsonSchemaReferenceand exposes them throughOpenApiSchemaReferenceconvenience getters using reference-first fallback behavior:This behavior appears to be intentional. In #2903, @baywet wrote after discussions with @handrews and @darrelmiller that:
And:
That explains why #2906 made the schema keyword getters reference-first across all keyword types.
The concern is that this convenience behavior can be easy to misread for JSON Schema assertion keywords.
In JSON Schema 2020-12,
$refsiblings are evaluated additively. They are not overrides of the referenced schema.Example:
Both constraints apply. The sibling
minProperties: 2does not relax the target'sminProperties: 5.With the current object model:
This can make assertion keywords appear to override target constraints, even though JSON Schema evaluation treats them compositionally.
Question
Should
OpenApiSchemaReferencegetters continue to expose reference-first convenience behavior for all schema keywords, or should the library distinguish more explicitly between:JsonSchemaReferenceTargetPossible Directions
Option 1: Keep current behavior and document it
Keep
Reference.X ?? Target?.Xfor all schema keyword getters.Document that these getters are object-model convenience accessors, not a JSON Schema evaluation/effective-schema view. Consumers that care about conflicts or additive constraints should inspect both
schemaRef.ReferenceandschemaRef.Target.Option 2: Split annotation and assertion getter behavior
Keep reference-first getters for annotation-style keywords such as:
DescriptionTitleDefaultDeprecatedReadOnlyWriteOnlyExamplesExtensionsReturn target values for assertion/structural keywords such as:
TypeFormatMinPropertiesMaxPropertiesPatternRequiredPropertiesAllOfOneOfAnyOfNotItemsIfThenElseThis is more validation-oriented, but it breaks the uniform getter rule from #2903 and creates setter/getter asymmetry. For example, setting
schemaRef.Typestores the sibling value onReference, but readingschemaRef.Typewould return the target value.Option 3: Keep current getters and add an explicit evaluation/composition API later
Keep the current object-model behavior for compatibility and consistency.
Add a future API such as
GetEffectiveSchema()or similar for consumers that need JSON Schema evaluation semantics. That API could preserve additive sibling assertions compositionally, for example by representing inlined references withallOfrather than flattening sibling assertions as overrides.Related Concern: Inline Serialization
CopyReferenceAsTargetElementWithOverrides()currently creates an inlineOpenApiSchemafrom the reference view. Because the reference view is reference-first, inline serialization can flatten target-plus-sibling state in a way that looks like sibling assertions override target assertions.A semantically safer inline representation would preserve composition, for example:
This may be better handled separately from the getter behavior.
Related
$dynamicRefschemas.$dynamicRef/$dynamicAnchorresolution.