From 77d74e7b46a7faa2d260c69af2d2c58d834708b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mu=C3=B1oz?= Date: Thu, 29 Oct 2026 12:06:47 +0100 Subject: [PATCH 1/4] Fix null defaults for Spring query parameters --- .../codegen/languages/SpringCodegen.java | 11 ++++ .../java/spring/SpringCodegenTest.java | 44 ++++++++++++++++ .../spring/optional-query-null-default.yaml | 52 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java index cb32ed5a0266..2febb3917ddb 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java @@ -17,6 +17,7 @@ package org.openapitools.codegen.languages; +import com.fasterxml.jackson.databind.JsonNode; import com.github.javaparser.StaticJavaParser; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; @@ -93,6 +94,16 @@ public class SpringCodegen extends AbstractJavaCodegen public static final String SKIP_DEFAULT_INTERFACE = "skipDefaultInterface"; public static final String GENERATE_CONSTRUCTOR_WITH_REQUIRED_ARGS = "generatedConstructorWithRequiredArgs"; + @Override + public String toDefaultParameterValue(Schema schema) { + Schema effectiveSchema = schema.get$ref() != null ? ModelUtils.getReferencedSchema(openAPI, schema) : schema; + Object defaultValue = effectiveSchema.getDefault(); + if (defaultValue instanceof JsonNode && ((JsonNode) defaultValue).isNull()) { + return null; + } + return super.toDefaultParameterValue(schema); + } + public static final String RESOURCE_FOLDER = "resourceFolder"; public static final String RESOURCE_FOLDER_DESC = "resource folder for generated resources"; diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index 144bb6927b96..00ed30b4b99f 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -3029,6 +3029,50 @@ public void shouldIgnorePageableForSpringHttpInterface_issue24720() throws IOExc .doesNotHaveParameter("pageable"); } + @Test + public void nullSchemaDefaultIsNotRenderedAsSpringRequestParamDefault() throws IOException { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(INTERFACE_ONLY, true); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, true); + additionalProperties.put(USE_SPRING_BOOT4, true); + additionalProperties.put("useJackson3", true); + additionalProperties.put(USE_JSPECIFY, true); + additionalProperties.put(USE_DEDUCTION_FOR_ONE_OF_INTERFACES, true); + additionalProperties.put(AbstractJavaCodegen.CONTAINER_DEFAULT_TO_NULL, true); + additionalProperties.put(GENERATE_JSON_INCLUDE_ANNOTATIONS, true); + additionalProperties.put(PERFORM_BEANVALIDATION, true); + additionalProperties.put(AbstractJavaCodegen.OPENAPI_NULLABLE, false); + + Map files = generateFromContract( + "src/test/resources/3_1/spring/optional-query-null-default.yaml", SPRING_HTTP_INTERFACE, additionalProperties); + + JavaFileAssert api = JavaFileAssert.assertThat(files.get("PlacesApi.java")); + api.assertMethod("getPlaces") + .assertParameter("ids").hasType("List") + .assertParameterAnnotations() + .containsWithNameAndDoesNotContainAttributes("RequestParam", List.of("defaultValue")); + api.assertMethod("getPlaces") + .assertParameter("search") + .assertParameterAnnotations() + .containsWithNameAndDoesNotContainAttributes("RequestParam", List.of("defaultValue")); + api.assertMethod("getPlaces") + .assertParameter("limit") + .assertParameterAnnotations() + .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"10\"")); + api.assertMethod("getPlaces") + .assertParameter("requiredIds") + .assertParameterAnnotations() + .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("required", "true")); + api.assertMethod("getPlaces") + .assertParameter("defaultIds") + .assertParameterAnnotations() + .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"a,b\"")); + api.assertMethod("getPlaces") + .assertParameter("literalNull") + .assertParameterAnnotations() + .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"null\"")); + } + @Test public void explicitXSpringPaginatedFalseKeepsQueryParamsForSpringCloud_issue24720() throws IOException { // Regression #24720: x-spring-paginated: false on a spring-cloud operation must NOT be diff --git a/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml new file mode 100644 index 000000000000..6637bf8b7a88 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml @@ -0,0 +1,52 @@ +openapi: 3.1.0 +info: + title: Optional query null default + version: 1.0.0 +paths: + /places: + get: + operationId: getPlaces + parameters: + - name: ids + in: query + required: false + schema: + type: array + items: + type: string + default: null + - name: search + in: query + required: false + schema: + type: string + - name: limit + in: query + required: false + schema: + type: integer + default: 10 + - name: requiredIds + in: query + required: true + schema: + type: array + items: + type: string + - name: defaultIds + in: query + required: false + schema: + type: array + items: + type: string + default: [a, b] + - name: literalNull + in: query + required: false + schema: + type: string + default: "null" + responses: + "200": + description: OK From 0702c2abe12701030c1f1cd3e1bae82c36d57902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mu=C3=B1oz?= Date: Thu, 29 Oct 2026 12:44:11 +0100 Subject: [PATCH 2/4] Preserve parameter defaults on schema references --- .../openapitools/codegen/languages/SpringCodegen.java | 2 +- .../codegen/java/spring/SpringCodegenTest.java | 4 ++++ .../3_1/spring/optional-query-null-default.yaml | 11 +++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java index 2febb3917ddb..4621879cc917 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java @@ -97,7 +97,7 @@ public class SpringCodegen extends AbstractJavaCodegen @Override public String toDefaultParameterValue(Schema schema) { Schema effectiveSchema = schema.get$ref() != null ? ModelUtils.getReferencedSchema(openAPI, schema) : schema; - Object defaultValue = effectiveSchema.getDefault(); + Object defaultValue = schema.getDefault() != null ? schema.getDefault() : effectiveSchema.getDefault(); if (defaultValue instanceof JsonNode && ((JsonNode) defaultValue).isNull()) { return null; } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index 00ed30b4b99f..e2dcc196eaa6 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -3071,6 +3071,10 @@ public void nullSchemaDefaultIsNotRenderedAsSpringRequestParamDefault() throws I .assertParameter("literalNull") .assertParameterAnnotations() .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"null\"")); + api.assertMethod("getPlaces") + .assertParameter("referencedLiteralNull") + .assertParameterAnnotations() + .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"null\"")); } @Test diff --git a/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml index 6637bf8b7a88..f773d1705401 100644 --- a/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml +++ b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml @@ -47,6 +47,17 @@ paths: schema: type: string default: "null" + - name: referencedLiteralNull + in: query + required: false + schema: + $ref: '#/components/schemas/stringWithNullDefault' + default: "null" responses: "200": description: OK +components: + schemas: + stringWithNullDefault: + type: string + default: null From f9c19a1055f483978a2aad1700d6fd3f22e69652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mu=C3=B1oz?= Date: Thu, 29 Oct 2026 13:03:21 +0100 Subject: [PATCH 3/4] Cover referenced null defaults without overrides --- .../openapitools/codegen/java/spring/SpringCodegenTest.java | 4 ++++ .../resources/3_1/spring/optional-query-null-default.yaml | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index e2dcc196eaa6..398850ed9b5b 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -3075,6 +3075,10 @@ public void nullSchemaDefaultIsNotRenderedAsSpringRequestParamDefault() throws I .assertParameter("referencedLiteralNull") .assertParameterAnnotations() .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"null\"")); + api.assertMethod("getPlaces") + .assertParameter("referencedNullDefault") + .assertParameterAnnotations() + .containsWithNameAndDoesNotContainAttributes("RequestParam", List.of("defaultValue")); } @Test diff --git a/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml index f773d1705401..7fd268572cf2 100644 --- a/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml +++ b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml @@ -53,6 +53,11 @@ paths: schema: $ref: '#/components/schemas/stringWithNullDefault' default: "null" + - name: referencedNullDefault + in: query + required: false + schema: + $ref: '#/components/schemas/stringWithNullDefault' responses: "200": description: OK From 19ac4e248dc11060c43ac00475f8375ea2483241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mu=C3=B1oz?= Date: Tue, 3 Nov 2026 13:38:28 +0100 Subject: [PATCH 4/4] Move null parameter default handling to Java base --- .../codegen/languages/AbstractJavaCodegen.java | 6 +++++- .../openapitools/codegen/languages/SpringCodegen.java | 11 ----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index a94d4d7ff025..0dee6e7ec8d7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -1749,10 +1749,14 @@ private String getDefaultCollectionType(String arrayFormat, String defaultValues @Override public String toDefaultParameterValue(final Schema schema) { - Object defaultValue = schema.get$ref() != null ? ModelUtils.getReferencedSchema(openAPI, schema).getDefault() : schema.getDefault(); + Schema referencedSchema = schema.get$ref() != null ? ModelUtils.getReferencedSchema(openAPI, schema) : schema; + Object defaultValue = schema.getDefault() != null ? schema.getDefault() : referencedSchema.getDefault(); if (defaultValue == null) { return null; } + if (isNullNode(defaultValue)) { + return null; + } if (defaultValue instanceof Date) { Date date = (Date) schema.getDefault(); LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java index 4621879cc917..cb32ed5a0266 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java @@ -17,7 +17,6 @@ package org.openapitools.codegen.languages; -import com.fasterxml.jackson.databind.JsonNode; import com.github.javaparser.StaticJavaParser; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; @@ -94,16 +93,6 @@ public class SpringCodegen extends AbstractJavaCodegen public static final String SKIP_DEFAULT_INTERFACE = "skipDefaultInterface"; public static final String GENERATE_CONSTRUCTOR_WITH_REQUIRED_ARGS = "generatedConstructorWithRequiredArgs"; - @Override - public String toDefaultParameterValue(Schema schema) { - Schema effectiveSchema = schema.get$ref() != null ? ModelUtils.getReferencedSchema(openAPI, schema) : schema; - Object defaultValue = schema.getDefault() != null ? schema.getDefault() : effectiveSchema.getDefault(); - if (defaultValue instanceof JsonNode && ((JsonNode) defaultValue).isNull()) { - return null; - } - return super.toDefaultParameterValue(schema); - } - public static final String RESOURCE_FOLDER = "resourceFolder"; public static final String RESOURCE_FOLDER_DESC = "resource folder for generated resources";