From 0e60e9861831be2e988750ecc5eb8004df354240 Mon Sep 17 00:00:00 2001 From: Mattias-Sehlstedt <60173714+Mattias-Sehlstedt@users.noreply.github.com> Date: Fri, 13 Mar 2026 12:17:27 +0100 Subject: [PATCH] fix: handle default values for LocalDate --- .../core/service/AbstractRequestService.java | 51 ++++++++----------- .../core/utils/SpringDocAnnotationsUtils.java | 27 ++++++++++ .../test/resources/results/3.0.1/app150.json | 3 +- .../test/resources/results/3.0.1/app150.json | 3 +- 4 files changed, 51 insertions(+), 33 deletions(-) diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java index 54609b445..fd8578ff1 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java @@ -36,27 +36,13 @@ import java.lang.reflect.Method; import java.security.Principal; import java.time.ZoneId; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.TimeZone; import java.util.stream.Collectors; import java.util.stream.Stream; import com.fasterxml.jackson.annotation.JsonView; import io.swagger.v3.core.converter.AnnotatedType; -import io.swagger.v3.core.util.PrimitiveType; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.models.Components; @@ -416,8 +402,12 @@ else if (!RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1. * @param parametersDocMap the parameters doc map * @return the parameter linked hash map */ - private LinkedHashMap getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List operationParameters, Map parametersDocMap) { - LinkedHashMap map = operationParameters.stream().collect(Collectors.toMap(ParameterId::new, parameter -> parameter, (u, v) -> { + private LinkedHashMap getParameterLinkedHashMap(Components components, + MethodAttributes methodAttributes, + List operationParameters, + Map parametersDocMap) { + LinkedHashMap map = operationParameters.stream() + .collect(Collectors.toMap(ParameterId::new, parameter -> parameter, (u, v) -> { LOGGER.warn( "Duplicate OpenAPI parameter detected: name='{}', in='{}'. Keeping the first found and ignoring the rest. " + "Declare the parameter only once.", @@ -439,7 +429,8 @@ private LinkedHashMap getParameterLinkedHashMap(Componen long mumParamsWithName = map.keySet().stream().filter(parameterId1 -> parameterId.getpName().equals(parameterId1.getpName())).count(); long mumParamsDocWithName = parametersDocMap.keySet().stream().filter(parameterId1 -> parameterId.getpName().equals(parameterId1.getpName())).count(); if (mumParamsWithName == 1 && mumParamsDocWithName == 1) { - Optional parameterIdWithSameNameOptional = map.keySet().stream().filter(parameterId1 -> parameterId.getpName().equals(parameterId1.getpName())).findAny(); + Optional parameterIdWithSameNameOptional = map.keySet().stream() + .filter(parameterId1 -> parameterId.getpName().equals(parameterId1.getpName())).findAny(); parameterIdWithSameNameOptional.ifPresent(parameterIdWithSameName -> { GenericParameterService.mergeParameter(map.get(parameterIdWithSameName), parameter); map.put(parameterIdWithSameName, parameter); @@ -574,7 +565,8 @@ private void setParams(Operation operation, List operationParameters, * @return the boolean */ public boolean isValidParameter(Parameter parameter, MethodAttributes methodAttributes) { - return parameter != null && (parameter.getName() != null || parameter.get$ref() != null) && !(ArrayUtils.contains(methodAttributes.getMethodConsumes(), APPLICATION_FORM_URLENCODED_VALUE) && ParameterIn.QUERY.toString().equals(parameter.getIn())); + return parameter != null && (parameter.getName() != null || parameter.get$ref() != null) && + !(ArrayUtils.contains(methodAttributes.getMethodConsumes(), APPLICATION_FORM_URLENCODED_VALUE) && ParameterIn.QUERY.toString().equals(parameter.getIn())); } /** @@ -639,14 +631,7 @@ public Parameter buildParam(ParameterInfo parameterInfo, Components components, Schema schema = parameterBuilder.calculateSchema(components, parameterInfo, null, jsonView); if (parameterInfo.getDefaultValue() != null && schema != null) { - Object defaultValue = parameterInfo.getDefaultValue(); - // Cast default value - PrimitiveType primitiveType = PrimitiveType.fromTypeAndFormat(schema.getType(), schema.getFormat()); - if (primitiveType != null) { - Schema primitiveSchema = primitiveType.createProperty(); - primitiveSchema.setDefault(parameterInfo.getDefaultValue()); - defaultValue = primitiveSchema.getDefault(); - } + Object defaultValue = SpringDocAnnotationsUtils.castDefaultValue(schema, parameterInfo.getDefaultValue()); schema.setDefault(defaultValue); } parameter.setSchema(schema); @@ -759,18 +744,22 @@ private Map getApiParamete Class declaringClass = method.getDeclaringClass(); Set apiParametersDoc = AnnotatedElementUtils.findAllMergedAnnotations(method, Parameters.class); - LinkedHashMap apiParametersMap = apiParametersDoc.stream().flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); + LinkedHashMap apiParametersMap = apiParametersDoc.stream() + .flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); Set apiParametersDocDeclaringClass = AnnotatedElementUtils.findAllMergedAnnotations(declaringClass, Parameters.class); - LinkedHashMap apiParametersDocDeclaringClassMap = apiParametersDocDeclaringClass.stream().flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); + LinkedHashMap apiParametersDocDeclaringClassMap = apiParametersDocDeclaringClass.stream() + .flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); apiParametersMap.putAll(apiParametersDocDeclaringClassMap); Set apiParameterDoc = AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.Parameter.class); - LinkedHashMap apiParameterDocMap = apiParameterDoc.stream().collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); + LinkedHashMap apiParameterDocMap = apiParameterDoc.stream() + .collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); apiParametersMap.putAll(apiParameterDocMap); Set apiParameterDocDeclaringClass = AnnotatedElementUtils.findAllMergedAnnotations(declaringClass, io.swagger.v3.oas.annotations.Parameter.class); - LinkedHashMap apiParameterDocDeclaringClassMap = apiParameterDocDeclaringClass.stream().collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); + LinkedHashMap apiParameterDocDeclaringClassMap = apiParameterDocDeclaringClass.stream() + .collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new)); apiParametersMap.putAll(apiParameterDocDeclaringClassMap); return apiParametersMap; diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java index 770cf372a..4648f3933 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocAnnotationsUtils.java @@ -29,6 +29,7 @@ import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -50,6 +51,7 @@ import io.swagger.v3.core.converter.ModelConverters; import io.swagger.v3.core.converter.ResolvedSchema; import io.swagger.v3.core.util.AnnotationsUtils; +import io.swagger.v3.core.util.PrimitiveType; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.media.Encoding; import io.swagger.v3.oas.annotations.media.ExampleObject; @@ -468,6 +470,31 @@ private static boolean isArray(io.swagger.v3.oas.annotations.media.Content annot return isArray; } + /** + * Attempt to cast the default value so that it matches the {@link Schema} type. + * If the value cannot be cast then the provided default value is returned as-is. + * + * @param schema the schema that will have the default value + * @param defaultValue the default value + * @return the cast default value + */ + public static Object castDefaultValue(Schema schema, Object defaultValue) { + if (schema != null && defaultValue != null) { + PrimitiveType primitiveType = PrimitiveType.fromTypeAndFormat(schema.getType(), schema.getFormat()); + if (primitiveType != null) { + Schema primitiveSchema = primitiveType.createProperty(); + if (primitiveType.equals(PrimitiveType.DATE) && defaultValue instanceof LocalDate localDate) { + defaultValue = localDate.toString(); + } + primitiveSchema.setDefault(defaultValue); + if (primitiveSchema.getDefault() != null) { + defaultValue = primitiveSchema.getDefault(); + } + } + } + return defaultValue; + } + /** * Resolve default value object. * diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app150.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app150.json index 95c0997c1..6649e5c3c 100644 --- a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app150.json +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app150.json @@ -24,7 +24,8 @@ "required": false, "schema": { "type": "string", - "format": "date" + "format": "date", + "default" : "2021-03-08" } } ], diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/3.0.1/app150.json b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/3.0.1/app150.json index e692a44a3..ff62baf22 100644 --- a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/3.0.1/app150.json +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/3.0.1/app150.json @@ -121,7 +121,8 @@ "required": false, "schema": { "type": "string", - "format": "date" + "format": "date", + "default": "2021-03-08" } } ],