Repository navigation
fix: keep oneOf when WRAPPER_OBJECT wrapping is applied - #5331
Conversation
|
Thank you for the PR @vpelikh! I tested this and pushed a small change on top: I removed the empty placeholder property, because it added noise to the schema. I also added tests (no extra property next to Before the PR, Pet:
type: object
properties:
Pet:
type: object
properties:
petType: {type: string}
oneOf: [Dog, Cat]After the PR, as submitted: Pet:
type: object
properties:
petType: {type: string}
Pet: {type: object} # empty placeholder
oneOf: [Dog, Cat]The The schema is still not fully correct. With {"Dog": {"petType": "dog", "bark": "woof"}}To match it, each Pet:
oneOf:
- type: object
required: [Dog]
properties:
Dog: {$ref: '#/components/schemas/Dog'}
- type: object
required: [Cat]
properties:
Cat: {$ref: '#/components/schemas/Cat'}That needs a more complex handler (subtype names, |
|
Hey @ewaostrowska. Thanks for reviewing this and other PRs. Are you okay with this PR as is, or would you like me to make some changes? |
a7bcd87 to
a5ecee0
Compare
|
@vpelikh I think the current state should be fine for now, but let's wait on review from @djankows or @daniel-kmiecik |
There was a problem hiding this comment.
🟡 Changes recommended
The implementation preserves oneOf by removing required WRAPPER_OBJECT semantics, producing a schema that does not match serialized values.
1 open finding
What changed in this PR
Attempts to preserve oneOf schemas during Jackson WRAPPER_OBJECT resolution.
Changes:
- Bypasses wrapper creation when
oneOfis present. - Adds regression tests for OpenAPI 3.0 and 3.1.
| File | Description |
|---|---|
ModelResolver.java |
Alters wrapping behavior for composed schemas. |
WrapperObjectOneOfTest.java |
Tests oneOf preservation and repeated resolution. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
ModelResolver.resolveWrapping() nested the whole model inside a new wrapper schema that only copied the name and properties, so a model carrying a oneOf (from @Schema(oneOf) or @JsonSubTypes) lost its composition and became a plain object schema with a single property. When the model already has a non-empty oneOf, add the wrapper property directly to the model and return it, preserving the composition. Behavior is unchanged for models without oneOf. Adds WrapperObjectOneOfTest, which fails before the fix (ObjectSchema with no oneOf) and passes after it.
a5ecee0 to
fdce351
Compare

Summary
A schema that already carries a
oneOfnow keeps it when Jackson'sWRAPPER_OBJECTtype info is applied. Previously the composition was silently dropped and the schema became a plain object with a single property.Problem
ModelResolver.resolveWrapping(...)builds a new wrapper schema and nests the model inside it:The wrapper copies only the name and the single property. If
modelhad aoneOf(from@Schema(oneOf = ...),@Schema(subTypes = ...)or@JsonSubTypes), it is lost: the returned schema is anObjectSchema, not aComposedSchema, and theoneOfis gone.Reproduction (
@JsonTypeInfo(include = WRAPPER_OBJECT)+@Schema(oneOf)):ObjectSchema, properties[Pet], nooneOfComposedSchema,oneOf= 2 refs, wrapper property presentChange
When the model already has a non-empty
oneOf, add the wrapper property directly to the model and return it, instead of nesting it in a wrapper schema:The empty-object-schema expression was already used once in this method, so instead of repeating it a second time it is extracted to a small helper:
Behavior is unchanged for models without
oneOf.Tests
Adds
WrapperObjectOneOfTest. It fails before the change (ObjectSchemawith nooneOf) and passes after it.Notes
This is the small stack split out of #5320. It is a standalone bug fix, reproducible on unmodified
3.0.0, and does not depend on@JsonSubTypeswork.