Skip to content

[JAVA, JAVA-SPRING, KOTLIN-CLIENT, KOTLIN-SPRING] - feature - add x-jackson-default-impl / typeInfoDefaultImpls support to allow specifying of fallback schema when deserializing - #23955

Open
Picazsoo wants to merge 22 commits into
OpenAPITools:masterfrom
Picazsoo:feature/use-deduction-default
Open

[JAVA, JAVA-SPRING, KOTLIN-CLIENT, KOTLIN-SPRING] - feature - add x-jackson-default-impl / typeInfoDefaultImpls support to allow specifying of fallback schema when deserializing#23955
Picazsoo wants to merge 22 commits into
OpenAPITools:masterfrom
Picazsoo:feature/use-deduction-default

Conversation

@Picazsoo

@Picazsoo Picazsoo commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Purpose

Complements the current oneOf Jackson polymorphism by making it possible to supply a default fallback value for deserialization. This is useful e.g. when you want to specify a default for deduction-based polymorphism (FasterXML/jackson-databind#3055), or when you want to fall back to some no-op class.

This feature is fully opt-in and has no effect on existing usage. Generated output is unchanged unless you explicitly set x-jackson-default-impl or typeInfoDefaultImpls.

What it adds

Emits @JsonTypeInfo(defaultImpl = ...) on polymorphic oneOf interfaces. Two opt-in ways to configure it (both default to disabled):

  • Vendor extension x-jackson-default-impl: <ClassName> on a schema.
  • Generator option typeInfoDefaultImpls: {SchemaName: ClassName}, which overrides the vendor extension when both are set for the same schema.

Class names are resolved via toModelName (honoring model mappings and DTO prefix/suffix); warns on overrides and unknown targets.

Scope

Generator Support
java (client) deduction + discriminator
spring deduction + discriminator
kotlin-spring deduction + discriminator
kotlin (client) discriminator only
java-microprofile disabled (template doesn't route through oneOf interface)

Resolution is centralized in AbstractJavaCodegen (shared by Java + Spring). Templates only emit defaultImpl when a value is resolved, so output is byte-for-byte identical to before for anyone who doesn't opt in.

Tests & docs

  • Tests added for Java client, Spring, kotlin-spring, and kotlin client, covering deduction/discriminator cases, config override, model-name suffix handling, and a noDefaultImplWhenNeitherSourceIsSet case that confirms no change when unconfigured.
  • New shared test spec 3_0/spring/jackson-default-impl.yaml.
  • Docs updated for java, spring, kotlin-spring, kotlin, and java-camel; new x-jackson-default-impl vendor extension registered.

PR checklist


Summary by cubic

Adds opt-in Jackson default deserialization for oneOf interfaces. Previously no default was set; now @JsonTypeInfo(defaultImpl=...) is emitted when configured, with no change when unconfigured.

  • Resolves x-jackson-default-impl and typeInfoDefaultImpls in AbstractJavaCodegen to x-jackson-resolved-default-impl, honoring toModelName, schema/import mappings, and DTO prefix/suffix. typeInfoDefaultImpls overrides the vendor extension and warns on shadowed/unknown targets.
  • Emission:
    • Java and Spring: defaultImpl for both Id.NAME (discriminator) and Id.DEDUCTION.
    • kotlin-spring: supports both deduction and discriminator.
    • kotlin client: discriminator-only.
    • java-microprofile: disabled.
  • Templates updated to include defaultImpl in type-info annotations across Java, Spring, kotlin-spring, and kotlin client.
  • Option registered in CodegenConstants (TYPE_INFO_DEFAULT_IMPLS) and documented across generators; added samples and CI for Java RestClient, Spring Boot, kotlin-spring, kotlin client, and java-camel.
  • Tests added for Java client, Spring, kotlin-spring, and kotlin client covering deduction/discriminator cases and config precedence.

Migration

  • To enable a default fallback, set x-jackson-default-impl: ClassName on a schema or configure typeInfoDefaultImpls: {SchemaName: ClassName}; the config option overrides the extension.
  • No action required if you do not opt in.

Written for commit 8d5365a. Summary will update on new commits.

Review in cubic

…foDefaultImpls support

   Emit `@JsonTypeInfo(defaultImpl = ...)` for both deduction-based and
   discriminator-based oneOf interfaces.

   Two configuration sources (config option takes precedence):
   - Schema extension: `x-jackson-default-impl: ClassName`
   - Config option: `typeInfoDefaultImpls: {SchemaName: ClassName}`

   The resolved class name passes through `toModelName()` so schemaMapping,
   importMapping, and DTO prefix/suffix are all honoured. A LOGGER.warn is
   emitted when the config option shadows a schema-level annotation.
@Picazsoo
Picazsoo marked this pull request as ready for review June 5, 2026 13:29
@Picazsoo
Picazsoo marked this pull request as draft June 5, 2026 13:29

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 9 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

public static final String USE_SEALED_RESPONSE_INTERFACES = "useSealedResponseInterfaces";
public static final String COMPANION_OBJECT = "companionObject";
public static final String SUSPEND_FUNCTIONS = "suspendFunctions";
public static final String TYPE_INFO_DEFAULT_IMPLS = "typeInfoDefaultImpls";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Move to CodegenConstants ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Picazsoo Could you also add the feature to the java generators?
See src/main/resources/Java/deductionAnnotation.mustache and the different typeInfoAnnotation.mustache under java/libraries

@Picazsoo Picazsoo Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I will take a look at it. And this is early implementation without any polish. I marked temporarily as "ready for review" just to gather early feedback from Cubic

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

But thanks for the review! I will properly tag you once I think it is ready for wasting human review time (-:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi @jpfinne, I added it to the java generators as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is now ready for review

Picazsoo and others added 4 commits June 17, 2026 23:18
# Conflicts:
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java
…Java generators

- Add TYPE_INFO_DEFAULT_IMPLS/DESC constants to CodegenConstants (addresses
  reviewer request to avoid duplication across SpringCodegen and
  KotlinSpringServerCodegen)
- Move typeInfoDefaultImpls field, option registration, processOpts parsing,
  and x-jackson-resolved-default-impl resolution loop from SpringCodegen into
  AbstractJavaCodegen so all Java generators inherit the feature
- Remove duplicated code from SpringCodegen; update KotlinSpringServerCodegen
  to reference CodegenConstants.TYPE_INFO_DEFAULT_IMPLS and apply the same
  safe instanceof cast fix already present in SpringCodegen
- Update Java/deductionAnnotation.mustache: emit defaultImpl = X.class on
  @JsonTypeInfo(use = DEDUCTION) when x-jackson-resolved-default-impl is set
- Update Java/typeInfoAnnotation.mustache: emit defaultImpl = X.class on both
  @JsonTypeInfo(use = NAME) variants (discriminator present/absent) via
  discriminator.vendorExtensions.x-jackson-resolved-default-impl
- Add JavaClientCodegenTest tests covering deduction, discriminator,
  typeInfoDefaultImpls config option, and absence cases for Java client

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Picazsoo Picazsoo changed the title feat(kotlin-spring, java-spring): add x-jackson-default-impl / typeInfoDefaultImpls support [kotlin-spring, java(-spring)] -feature - add x-jackson-default-impl / typeInfoDefaultImpls support to allow specifying of fallback schema when deserializing Jun 17, 2026
@Picazsoo Picazsoo changed the title [kotlin-spring, java(-spring)] -feature - add x-jackson-default-impl / typeInfoDefaultImpls support to allow specifying of fallback schema when deserializing [KOTLIN-SPRING, JAVA(-SPRING)] - feature - add x-jackson-default-impl / typeInfoDefaultImpls support to allow specifying of fallback schema when deserializing Jun 17, 2026
@Picazsoo
Picazsoo marked this pull request as ready for review June 17, 2026 22:07

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 17 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@Picazsoo Picazsoo changed the title [KOTLIN-SPRING, JAVA(-SPRING)] - feature - add x-jackson-default-impl / typeInfoDefaultImpls support to allow specifying of fallback schema when deserializing [JAVA, JAVA-SPRING, KOTLIN-SPRING] - feature - add x-jackson-default-impl / typeInfoDefaultImpls support to allow specifying of fallback schema when deserializing Jun 18, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

9 issues found across 27 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/kotlin-spring/typeInfoAnnotation.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-spring/typeInfoAnnotation.mustache:6">
P1: Missing validation that the resolved defaultImpl is a subtype of the polymorphic base; misconfigured `x-jackson-default-impl` or `typeInfoDefaultImpls` values will compile but fail at runtime during deserialization.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread docs/generators/java-play-framework.md Outdated
Comment thread docs/generators/java-micronaut-server.md Outdated
Comment thread docs/generators/jaxrs-spec.md Outdated
Comment thread docs/generators/java-undertow-server.md Outdated
Comment thread docs/generators/java-dubbo.md Outdated
Comment thread docs/generators/java-inflector.md
Comment thread docs/generators/java-wiremock.md Outdated
Comment thread docs/generators/java-microprofile.md Outdated
Comment thread docs/generators/java-play-framework.md Outdated
@Picazsoo Picazsoo changed the title [JAVA, JAVA-SPRING, KOTLIN-SPRING] - feature - add x-jackson-default-impl / typeInfoDefaultImpls support to allow specifying of fallback schema when deserializing [JAVA, JAVA-SPRING, KOTLIN-CLIENT, KOTLIN-SPRING] - feature - add x-jackson-default-impl / typeInfoDefaultImpls support to allow specifying of fallback schema when deserializing Jun 18, 2026
# Conflicts:
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java
@Picazsoo
Picazsoo requested a review from jpfinne July 1, 2026 12:48
@Picazsoo

Picazsoo commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Tagging @wing328 - please consider if this contribution makes sense and if it would make sense for 7.24.0

Picazsoo added 2 commits July 13, 2026 00:12
# Conflicts:
#	docs/generators/kotlin-spring.md
#	docs/generators/kotlin.md
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 333 files

Note: This PR contains a large number of files. cubic only reviews up to 200 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.

Re-trigger cubic

Comment thread docs/generators/kotlin.md Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 74 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

@Picazsoo

Picazsoo commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

Hi @wing328 , I wonder if this feature is acceptable or not as is?

Picazsoo and others added 2 commits August 13, 2026 20:21
…on-default

# Conflicts:
#	docs/generators/java-camel.md
#	docs/generators/spring.md
#	modules/openapi-generator/src/main/java/org/openapitools/codegen/VendorExtension.java
#	modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 issues found across 30 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/server/petstore/kotlin-spring-oneof-jackson-default-impl/src/main/kotlin/org/openapitools/model/Soda.kt">

<violation number="1">
P2: This sample regeneration removes `@JsonInclude(NON_NULL)` and `@JsonSetter(nulls = Nulls.SKIP)` from the optional nullable field `sugarGrams`, so the generated `Soda` model will now serialize `"sugarGrams": null` instead of dropping the key when the field is null. This null-handling change is unrelated to the defaultImpl feature and contradicts the PR's stated 'no behavioral change unless opt-in / byte-for-byte identical' guarantee. Confirm this is an intended generator-output change (and matches what the generator actually emits) rather than an unintended side effect of the sample regeneration; if the generator change is spurious it should be reverted in the templates, otherwise update the PR description which claims output is otherwise unchanged.</violation>
</file>

<file name="samples/openapi3/server/petstore/spring-boot-oneof-jackson-default-impl/src/main/java/org/openapitools/model/Apple.java">

<violation number="1">
P2: This regeneration drops `@JsonInclude(JsonInclude.Include.NON_NULL)` from the optional `seeds` field (spec `type: integer`, non-nullable). With the annotation removed, a null `seeds` is now serialized as `"seeds": null` where it was previously omitted. This contradicts the generator's own matrix (optional & non-nullable -> NON_NULL in JsonAnnotationPolicyUtils.resolveJsonIncludePolicy) and diverges from sibling samples (spring-boot-oneof `Pizza.pizzaSize` and spring-boot-springdoc `Tag.id` keep NON_NULL on equivalent optional fields). Restore the annotation and the JsonInclude import, or regenerate the sample under the same config as the sibling samples.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

@@ -0,0 +1,33 @@
package org.openapitools.model

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: This sample regeneration removes @JsonInclude(NON_NULL) and @JsonSetter(nulls = Nulls.SKIP) from the optional nullable field sugarGrams, so the generated Soda model will now serialize "sugarGrams": null instead of dropping the key when the field is null. This null-handling change is unrelated to the defaultImpl feature and contradicts the PR's stated 'no behavioral change unless opt-in / byte-for-byte identical' guarantee. Confirm this is an intended generator-output change (and matches what the generator actually emits) rather than an unintended side effect of the sample regeneration; if the generator change is spurious it should be reverted in the templates, otherwise update the PR description which claims output is otherwise unchanged.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/kotlin-spring-oneof-jackson-default-impl/src/main/kotlin/org/openapitools/model/Soda.kt, line 32:

<comment>This sample regeneration removes `@JsonInclude(NON_NULL)` and `@JsonSetter(nulls = Nulls.SKIP)` from the optional nullable field `sugarGrams`, so the generated `Soda` model will now serialize `"sugarGrams": null` instead of dropping the key when the field is null. This null-handling change is unrelated to the defaultImpl feature and contradicts the PR's stated 'no behavioral change unless opt-in / byte-for-byte identical' guarantee. Confirm this is an intended generator-output change (and matches what the generator actually emits) rather than an unintended side effect of the sample regeneration; if the generator change is spurious it should be reverted in the templates, otherwise update the PR description which claims output is otherwise unchanged.</comment>

<file context>
@@ -1,10 +1,7 @@
 package org.openapitools.model
 
 import java.util.Objects
-import com.fasterxml.jackson.annotation.JsonInclude
 import com.fasterxml.jackson.annotation.JsonProperty
-import com.fasterxml.jackson.annotation.JsonSetter
-import com.fasterxml.jackson.annotation.Nulls
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties
 import com.fasterxml.jackson.annotation.JsonSubTypes
</file context>

@@ -0,0 +1,121 @@
package org.openapitools.model;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: This regeneration drops @JsonInclude(JsonInclude.Include.NON_NULL) from the optional seeds field (spec type: integer, non-nullable). With the annotation removed, a null seeds is now serialized as "seeds": null where it was previously omitted. This contradicts the generator's own matrix (optional & non-nullable -> NON_NULL in JsonAnnotationPolicyUtils.resolveJsonIncludePolicy) and diverges from sibling samples (spring-boot-oneof Pizza.pizzaSize and spring-boot-springdoc Tag.id keep NON_NULL on equivalent optional fields). Restore the annotation and the JsonInclude import, or regenerate the sample under the same config as the sibling samples.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/openapi3/server/petstore/spring-boot-oneof-jackson-default-impl/src/main/java/org/openapitools/model/Apple.java, line 31:

<comment>This regeneration drops `@JsonInclude(JsonInclude.Include.NON_NULL)` from the optional `seeds` field (spec `type: integer`, non-nullable). With the annotation removed, a null `seeds` is now serialized as `"seeds": null` where it was previously omitted. This contradicts the generator's own matrix (optional & non-nullable -> NON_NULL in JsonAnnotationPolicyUtils.resolveJsonIncludePolicy) and diverges from sibling samples (spring-boot-oneof `Pizza.pizzaSize` and spring-boot-springdoc `Tag.id` keep NON_NULL on equivalent optional fields). Restore the annotation and the JsonInclude import, or regenerate the sample under the same config as the sibling samples.</comment>

<file context>
@@ -28,7 +27,6 @@ public class Apple implements Fruit {
 
-  @JsonInclude(JsonInclude.Include.NON_NULL)
   private @Nullable Integer seeds;
 
   public Apple() {
</file context>

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