Skip to content

Feature/fix json include#24428

Open
Picazsoo wants to merge 12 commits into
OpenAPITools:masterfrom
Picazsoo:feature/fix-json-include
Open

Feature/fix json include#24428
Picazsoo wants to merge 12 commits into
OpenAPITools:masterfrom
Picazsoo:feature/fix-json-include

Conversation

@Picazsoo

@Picazsoo Picazsoo commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Summary by cubic

Make @JsonInclude and @JsonSetter(nulls=...) emission in spring and kotlin-spring opt-in and configurable, restoring 7.23.0-style output by default to avoid unexpected wire contract changes. Also emit @JsonSetter(nulls=...) on fields so it works when Lombok generates setters.

  • New Features

    • Add generateJsonIncludeAnnotations (opt-in) to control policy @JsonInclude on model properties; when unset, emit none and log a warning.
    • Add generateJsonSetterNullsAnnotations (opt-in) for @JsonSetter(nulls = ...) on optional non‑nullable props; when unset, emit none and log a warning. Annotation is now placed on fields to work with Lombok @Setter.
    • Add optionalNonNullPropertyJsonInclude with NON_NULL (default), NON_EMPTY, NON_DEFAULT, or NONE.
    • Support per-property override via x-jackson-json-include-policy (always wins); validate against Jackson Include values, accept trimmed NONE to emit nothing, and add imports only when an annotation is emitted.
    • Do not emit @JsonInclude(NON_ABSENT) for JsonNullable<T> fields.
  • Refactors

    • Centralize parsing/validation and option normalization in JsonIncludePolicyUtils; use CodegenConstants and VendorExtension.X_JACKSON_JSON_INCLUDE_POLICY across generators and templates.

Written for commit 3dbcd99. Summary will update on new commits.

Review in cubic

@Picazsoo
Picazsoo marked this pull request as ready for review July 24, 2026 13:36
@Picazsoo
Picazsoo marked this pull request as draft July 24, 2026 13:36

@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.

3 issues found across 10 files

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/JavaSpring/pojo.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache:66">
P1: A manual `x-jackson-json-include-policy: NONE` now generates an annotation even though `NONE` means no annotation; because `SpringCodegen` also skips the import for this value, generated Java fails to compile. Suppress the template section for `NONE` (or remove that extension before rendering).</violation>

<violation number="2" location="modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache:66">
P2: Invalid `x-jackson-json-include-policy` values now produce uncompilable Java/Kotlin output because the template inserts the extension directly as a `JsonInclude.Include` constant. Validating the override against the supported policies (or rejecting it during generator processing) would turn this into an actionable generation error instead of a downstream compile failure.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java:1356">
P2: With `openApiNullable=true` alone, explicit `null` on optional non-nullable fields now falls through to mapper defaults rather than failing. This changes the documented strict-null contract; retain `Nulls.FAIL` for that mode or make the changed behavior explicit and compatible for existing configurations.</violation>
</file>

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

Re-trigger cubic

@JsonInclude(JsonInclude.Include.NON_ABSENT)
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{#vendorExtensions.x-jackson-json-include-policy}}
@JsonInclude(JsonInclude.Include.{{{vendorExtensions.x-jackson-json-include-policy}}})

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.

P1: A manual x-jackson-json-include-policy: NONE now generates an annotation even though NONE means no annotation; because SpringCodegen also skips the import for this value, generated Java fails to compile. Suppress the template section for NONE (or remove that extension before rendering).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache, line 66:

<comment>A manual `x-jackson-json-include-policy: NONE` now generates an annotation even though `NONE` means no annotation; because `SpringCodegen` also skips the import for this value, generated Java fails to compile. Suppress the template section for `NONE` (or remove that extension before rendering).</comment>

<file context>
@@ -62,14 +62,9 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
-  @JsonInclude(JsonInclude.Include.NON_ABSENT)
-  {{/vendorExtensions.x-is-jackson-optional-nullable}}
+  {{#vendorExtensions.x-jackson-json-include-policy}}
+  @JsonInclude(JsonInclude.Include.{{{vendorExtensions.x-jackson-json-include-policy}}})
+  {{/vendorExtensions.x-jackson-json-include-policy}}
   {{/jackson}}
</file context>

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 was taken care of

// Always emit @JsonInclude(NON_NULL) so null fields are omitted from serialized output regardless
// of who is deserializing on the other end — closer to spec, avoids round-trip failures.
if (!property.required && !property.isNullable) {
if (Boolean.TRUE.equals(generateJsonSetterNullsAnnotations) && !property.required && !property.isNullable) {

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: With openApiNullable=true alone, explicit null on optional non-nullable fields now falls through to mapper defaults rather than failing. This changes the documented strict-null contract; retain Nulls.FAIL for that mode or make the changed behavior explicit and compatible for existing configurations.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java, line 1356:

<comment>With `openApiNullable=true` alone, explicit `null` on optional non-nullable fields now falls through to mapper defaults rather than failing. This changes the documented strict-null contract; retain `Nulls.FAIL` for that mode or make the changed behavior explicit and compatible for existing configurations.</comment>

<file context>
@@ -1280,20 +1349,18 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
-        // Always emit @JsonInclude(NON_NULL) so null fields are omitted from serialized output regardless
-        // of who is deserializing on the other end — closer to spec, avoids round-trip failures.
-        if (!property.required && !property.isNullable) {
+        if (Boolean.TRUE.equals(generateJsonSetterNullsAnnotations) && !property.required && !property.isNullable) {
             if (openApiNullable) {
                 property.vendorExtensions.put("x-has-json-setter-nulls-fail", true);
</file context>
Suggested change
if (Boolean.TRUE.equals(generateJsonSetterNullsAnnotations) && !property.required && !property.isNullable) {
if ((Boolean.TRUE.equals(generateJsonSetterNullsAnnotations) || openApiNullable) && !property.required && !property.isNullable) {

@JsonInclude(JsonInclude.Include.NON_ABSENT)
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{#vendorExtensions.x-jackson-json-include-policy}}
@JsonInclude(JsonInclude.Include.{{{vendorExtensions.x-jackson-json-include-policy}}})

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: Invalid x-jackson-json-include-policy values now produce uncompilable Java/Kotlin output because the template inserts the extension directly as a JsonInclude.Include constant. Validating the override against the supported policies (or rejecting it during generator processing) would turn this into an actionable generation error instead of a downstream compile failure.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache, line 66:

<comment>Invalid `x-jackson-json-include-policy` values now produce uncompilable Java/Kotlin output because the template inserts the extension directly as a `JsonInclude.Include` constant. Validating the override against the supported policies (or rejecting it during generator processing) would turn this into an actionable generation error instead of a downstream compile failure.</comment>

<file context>
@@ -62,14 +62,9 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
-  @JsonInclude(JsonInclude.Include.NON_ABSENT)
-  {{/vendorExtensions.x-is-jackson-optional-nullable}}
+  {{#vendorExtensions.x-jackson-json-include-policy}}
+  @JsonInclude(JsonInclude.Include.{{{vendorExtensions.x-jackson-json-include-policy}}})
+  {{/vendorExtensions.x-jackson-json-include-policy}}
   {{/jackson}}
</file context>

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 was taken care of

@Picazsoo
Picazsoo marked this pull request as ready for review July 24, 2026 14:00
@Picazsoo
Picazsoo marked this pull request as draft July 24, 2026 14:01

@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 10 files

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/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java:6910">
P2: The new invalid-override regression test cannot run because its referenced fixture `issue_24401_json_include_invalid_override.yaml` is not present in the repository. Adding the fixture (with `NOT_A_REAL_POLICY`) or removing the test reference is needed; otherwise the Kotlin Spring test suite fails unconditionally.</violation>
</file>

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

Re-trigger cubic

@Test(description = "Issue #24401 – invalid manual override fails fast (kotlin-spring)")
public void jsonInclude_manualOverride_invalid_failsWithActionableError() {
Throwable thrown = Assert.expectThrows(Throwable.class, () -> generateFromContract(
"src/test/resources/3_0/spring/issue_24401_json_include_invalid_override.yaml",

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: The new invalid-override regression test cannot run because its referenced fixture issue_24401_json_include_invalid_override.yaml is not present in the repository. Adding the fixture (with NOT_A_REAL_POLICY) or removing the test reference is needed; otherwise the Kotlin Spring test suite fails unconditionally.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java, line 6910:

<comment>The new invalid-override regression test cannot run because its referenced fixture `issue_24401_json_include_invalid_override.yaml` is not present in the repository. Adding the fixture (with `NOT_A_REAL_POLICY`) or removing the test reference is needed; otherwise the Kotlin Spring test suite fails unconditionally.</comment>

<file context>
@@ -6690,28 +6695,226 @@ public void requiredNullable_scenario3_optionalNonNullable_withJackson3() throws
+    @Test(description = "Issue #24401 – invalid manual override fails fast (kotlin-spring)")
+    public void jsonInclude_manualOverride_invalid_failsWithActionableError() {
+        Throwable thrown = Assert.expectThrows(Throwable.class, () -> generateFromContract(
+                "src/test/resources/3_0/spring/issue_24401_json_include_invalid_override.yaml",
+                Map.of(KotlinSpringServerCodegen.GENERATE_JSON_INCLUDE_ANNOTATIONS, "true")));
+
</file context>

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 was taken care of

@Picazsoo
Picazsoo marked this pull request as ready for review July 24, 2026 15:02
@Picazsoo
Picazsoo marked this pull request as draft July 24, 2026 15:02
@Picazsoo
Picazsoo marked this pull request as ready for review July 24, 2026 15:17

@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 14 files

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/java/org/openapitools/codegen/languages/SpringCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java:1277">
P2: Models using Lombok-generated setters ignore `generateJsonSetterNullsAnnotations=true`, so explicit JSON nulls can still overwrite optional non-nullable defaults. Render the `@JsonSetter` annotation on the field or otherwise support the Lombok setter path.</violation>
</file>

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

Re-trigger cubic

// setter so an explicit null in the payload does not overwrite the field's default. Only emitted when
// generateJsonSetterNullsAnnotations is explicitly enabled; otherwise deserialization defers to the mapper.
if (Boolean.TRUE.equals(generateJsonSetterNullsAnnotations) && !property.required && !property.isNullable && !openApiNullable) {
property.vendorExtensions.put("x-has-json-setter-nulls-skip", true);

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: Models using Lombok-generated setters ignore generateJsonSetterNullsAnnotations=true, so explicit JSON nulls can still overwrite optional non-nullable defaults. Render the @JsonSetter annotation on the field or otherwise support the Lombok setter path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java, line 1277:

<comment>Models using Lombok-generated setters ignore `generateJsonSetterNullsAnnotations=true`, so explicit JSON nulls can still overwrite optional non-nullable defaults. Render the `@JsonSetter` annotation on the field or otherwise support the Lombok setter path.</comment>

<file context>
@@ -1213,24 +1270,48 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
+        // setter so an explicit null in the payload does not overwrite the field's default. Only emitted when
+        // generateJsonSetterNullsAnnotations is explicitly enabled; otherwise deserialization defers to the mapper.
+        if (Boolean.TRUE.equals(generateJsonSetterNullsAnnotations) && !property.required && !property.isNullable && !openApiNullable) {
+            property.vendorExtensions.put("x-has-json-setter-nulls-skip", true);
+            model.imports.add("JsonSetter");
+            model.imports.add("Nulls");
</file context>

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.

@cubic-dev-ai, I think I took care of this issue. Please re-review

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 I have started the AI code review. It will take a few minutes to complete.

@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.

No issues found across 14 files

Re-trigger cubic

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