From 7329495742e75d8118454f14baa1595aa39afc89 Mon Sep 17 00:00:00 2001
From: Mattias-Sehlstedt <60173714+Mattias-Sehlstedt@users.noreply.github.com>
Date: Mon, 3 Aug 2026 13:25:48 +0200
Subject: [PATCH] Upgrade swagger-core from version 2.2.52 to 2.2.53
---
pom.xml | 2 +-
.../api/AbstractOpenApiResource.java | 19 --
.../configuration/SpringDocConfiguration.java | 14 --
.../PropertyNamingStrategyConverter.java | 168 ------------------
.../org/springdoc/core/utils/SchemaUtils.java | 26 ---
.../springdoc/core/utils/SpringDocUtils.java | 52 ------
.../api/v31/app25/NullKeyController.kt | 2 +
.../test/resources/results/3.1.0/app25.json | 48 +++--
8 files changed, 34 insertions(+), 297 deletions(-)
delete mode 100644 springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PropertyNamingStrategyConverter.java
diff --git a/pom.xml b/pom.xml
index cc36c0655..c350fb3a5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -59,7 +59,7 @@
0.11.0
1.5.0
- 2.2.52
+ 2.2.53
5.32.11
1.13.1
0.9.1
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java
index aa6bbae9e..15dd6d9ba 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java
@@ -403,8 +403,6 @@ protected OpenAPI getOpenApi(String serverBaseUrl, Locale locale) {
}
getPaths(mappingsMap, finalLocale, openAPI);
- removeNullKeyComponentProperties(openAPI);
-
if (springDocConfigProperties.isTrimKotlinIndent())
this.trimIndent(openAPI);
@@ -465,23 +463,6 @@ private Locale selectLocale(Locale inputLocale) {
return inputLocale == null ? Locale.getDefault() : inputLocale;
}
- /**
- * Removes {@code null}-keyed entries from the component schema properties. Swagger-core
- * inserts a {@code null} property key when resolving a {@code @JsonUnwrapped} member whose
- * content is a {@code $ref} (for example Spring HATEOAS {@code EntityModel.getContent()}
- * with HAL disabled), which otherwise breaks JSON serialization of the document.
- *
- * @param openAPI the open api
- */
- private static void removeNullKeyComponentProperties(OpenAPI openAPI) {
- if (openAPI.getComponents() == null || openAPI.getComponents().getSchemas() == null) {
- return;
- }
- for (Schema> schema : openAPI.getComponents().getSchemas().values()) {
- SpringDocUtils.removeNullKeyProperties(schema);
- }
- }
-
/**
* Indents are removed for properties that are mainly used as “explanations” using Open API.
*
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocConfiguration.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocConfiguration.java
index be414ae24..97bc6d08c 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocConfiguration.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocConfiguration.java
@@ -56,7 +56,6 @@
import org.springdoc.core.converters.OAS31ModelConverter;
import org.springdoc.core.converters.PolymorphicModelConverter;
import org.springdoc.core.converters.PropertyCustomizingConverter;
-import org.springdoc.core.converters.PropertyNamingStrategyConverter;
import org.springdoc.core.converters.ResponseSupportConverter;
import org.springdoc.core.converters.SchemaPropertyDeprecatingConverter;
import org.springdoc.core.converters.WebFluxSupportConverter;
@@ -295,19 +294,6 @@ PolymorphicModelConverter polymorphicModelConverter(ObjectMapperProvider objectM
return new PolymorphicModelConverter(objectMapperProvider);
}
- /**
- * Property naming strategy converter property naming strategy converter.
- *
- * @param objectMapperProvider the object mapper provider
- * @return the property naming strategy converter
- */
- @Bean
- @ConditionalOnMissingBean
- @Lazy(false)
- PropertyNamingStrategyConverter propertyNamingStrategyConverter(ObjectMapperProvider objectMapperProvider) {
- return new PropertyNamingStrategyConverter(objectMapperProvider);
- }
-
/**
* Open api builder open api builder.
*
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PropertyNamingStrategyConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PropertyNamingStrategyConverter.java
deleted file mode 100644
index d711e942c..000000000
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PropertyNamingStrategyConverter.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- *
- * *
- * * *
- * * * *
- * * * * *
- * * * * * * Copyright 2019-2026 the original author or authors.
- * * * * * *
- * * * * * * Licensed under the Apache License, Version 2.0 (the "License");
- * * * * * * you may not use this file except in compliance with the License.
- * * * * * * You may obtain a copy of the License at
- * * * * * *
- * * * * * * https://www.apache.org/licenses/LICENSE-2.0
- * * * * * *
- * * * * * * Unless required by applicable law or agreed to in writing, software
- * * * * * * distributed under the License is distributed on an "AS IS" BASIS,
- * * * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * * * * * * See the License for the specific language governing permissions and
- * * * * * * limitations under the License.
- * * * *
- * * *
- * *
- *
- */
-
-package org.springdoc.core.converters;
-
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import com.fasterxml.jackson.databind.BeanDescription;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
-import io.swagger.v3.core.converter.AnnotatedType;
-import io.swagger.v3.core.converter.ModelConverter;
-import io.swagger.v3.core.converter.ModelConverterContext;
-import io.swagger.v3.core.converter.ModelConverters;
-import io.swagger.v3.core.jackson.ModelResolver;
-import io.swagger.v3.oas.models.Components;
-import io.swagger.v3.oas.models.media.Schema;
-import org.springdoc.core.providers.ObjectMapperProvider;
-
-/**
- * Repairs schema property names that swagger-core leaves in their raw (untranslated) form
- * when a Jackson {@code PropertyNamingStrategy} (for example {@code SNAKE_CASE}) is in
- * effect.
- *
- *
- * swagger-core's {@code ModelResolver} carries a long-standing workaround
- * (swagger-core#415)
- * that overwrites the Jackson-translated property name with the raw member name whenever
- * that member name starts with {@code get}/{@code is} followed by a lower-case character.
- * For Java records the accessor is named exactly like the component (e.g.
- * {@code issuanceDate()}), so a component such as {@code issuanceDate} is clobbered back
- * to camelCase while sibling properties like {@code familyName} convert correctly. This
- * converter restores the translated name.
- *
- *
- *
- * The mapper used by the active {@code ModelResolver} is consulted so that the fix is
- * self-consistent: it only renames a property when Jackson actually maps its internal
- * name to a different external name and swagger-core emitted the internal name.
- *
- *
- * See: springdoc-openapi#3293
- *
- * @author bnasslahsen
- */
-public class PropertyNamingStrategyConverter implements ModelConverter {
-
- /**
- * The Spring doc object mapper.
- */
- private final ObjectMapperProvider springDocObjectMapper;
-
- /**
- * Instantiates a new Property naming strategy converter.
- * @param springDocObjectMapper the spring doc object mapper
- */
- public PropertyNamingStrategyConverter(ObjectMapperProvider springDocObjectMapper) {
- this.springDocObjectMapper = springDocObjectMapper;
- }
-
- @Override
- public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator chain) {
- if (!chain.hasNext())
- return null;
- Schema> resolvedSchema = chain.next().resolve(type, context, chain);
-
- ObjectMapper mapper = resolverObjectMapper();
- if (mapper == null)
- return resolvedSchema;
-
- Schema> targetSchema = resolvedSchema;
- if (resolvedSchema != null && resolvedSchema.get$ref() != null)
- targetSchema = context.getDefinedModels()
- .get(resolvedSchema.get$ref().substring(Components.COMPONENTS_SCHEMAS_REF.length()));
-
- if (targetSchema == null || targetSchema.getProperties() == null)
- return resolvedSchema;
-
- JavaType javaType = mapper.constructType(type.getType());
- if (javaType == null || javaType.getRawClass().getPackageName().startsWith("java."))
- return resolvedSchema;
-
- renameClobberedProperties(targetSchema, mapper, javaType);
- return resolvedSchema;
- }
-
- /**
- * Renames properties that swagger-core emitted under their raw member name back to
- * the name Jackson computed from the active naming strategy, preserving property
- * order and the required list.
- * @param schema the schema to fix
- * @param mapper the object mapper used by the active model resolver
- * @param javaType the resolved java type
- */
- private void renameClobberedProperties(Schema> schema, ObjectMapper mapper, JavaType javaType) {
- BeanDescription beanDescription;
- try {
- beanDescription = mapper.getSerializationConfig().introspect(javaType);
- }
- catch (Exception e) {
- return;
- }
-
- Map renames = new LinkedHashMap<>();
- for (BeanPropertyDefinition property : beanDescription.findProperties()) {
- String externalName = property.getName();
- String internalName = property.getInternalName();
- if (externalName != null && !externalName.equals(internalName)
- && schema.getProperties().containsKey(internalName)
- && !schema.getProperties().containsKey(externalName)) {
- renames.put(internalName, externalName);
- }
- }
- if (renames.isEmpty())
- return;
-
- Map renamedProperties = new LinkedHashMap<>();
- schema.getProperties().forEach((name, value) -> renamedProperties.put(renames.getOrDefault(name, name), value));
- schema.setProperties(renamedProperties);
-
- List required = schema.getRequired();
- if (required != null)
- required.replaceAll(name -> renames.getOrDefault(name, name));
- }
-
- /**
- * Returns the {@link ObjectMapper} of the active swagger-core {@code ModelResolver},
- * which is the mapper that applied (or did not apply) the naming strategy while
- * generating the schema.
- * @return the object mapper, or {@code null} if none could be located
- */
- private ObjectMapper resolverObjectMapper() {
- for (ModelConverter converter : ModelConverters.getInstance(springDocObjectMapper.isOpenapi31())
- .getConverters()) {
- if (converter instanceof ModelResolver modelResolver)
- return modelResolver.objectMapper();
- }
- return null;
- }
-
-}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SchemaUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SchemaUtils.java
index e4ad206d3..ff7a6d4e5 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SchemaUtils.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SchemaUtils.java
@@ -21,7 +21,6 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode;
-import io.swagger.v3.oas.models.SpecVersion;
import io.swagger.v3.oas.models.media.Schema;
import jakarta.validation.OverridesAttribute;
import jakarta.validation.constraints.DecimalMax;
@@ -307,7 +306,6 @@ else if (OPENAPI_STRING_TYPE.equals(type)) {
schema.setMaximum(BigDecimal.valueOf(((Range) anno).max()));
}
});
- fixOAS31ExclusiveConstraints(schema);
if (schema!=null && annotatedNotNull(annotations)) {
String specVersion = schema.getSpecVersion().name();
if (!"V30".equals(specVersion)) {
@@ -576,28 +574,4 @@ private static JsonProperty getJsonProperty(Field f) {
if (g != null) return g.getAnnotation(JsonProperty.class);
return null;
}
-
- /**
- * Swagger-core 2.2.49 introduced so that {@link Positive} and {@link Negative} are introspected.
- * It does not correctly use the fact that exclusiveMinimum/exclusiveMaximum are values in OAS31.
- *
- * Tracked under swagger-core#5170.
- *
- * @param schema the schema to fix
- */
- public static void fixOAS31ExclusiveConstraints(Schema> schema) {
- if (schema == null) {
- return;
- }
- if (schema.getSpecVersion().equals(SpecVersion.V31)) {
- if (schema.getExclusiveMaximumValue() != null && schema.getMaximum() != null
- && schema.getMaximum().compareTo(schema.getExclusiveMaximumValue()) == 0) {
- schema.setMaximum(null);
- }
- if (schema.getExclusiveMinimumValue() != null && schema.getMinimum() != null &&
- schema.getMinimum().compareTo(schema.getExclusiveMinimumValue()) == 0) {
- schema.setMinimum(null);
- }
- }
- }
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocUtils.java
index ae2fe78e6..594b95229 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocUtils.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocUtils.java
@@ -31,7 +31,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
import java.util.function.Predicate;
import com.fasterxml.jackson.core.type.TypeReference;
@@ -181,60 +180,9 @@ else if (schema.getItems() != null && schema.getItems().getType() != null
if (schema.getProperties() != null) {
schema.getProperties().forEach((key, value) -> handleSchemaTypes(value));
}
- fixNullOnlyAdditionalProperties(schema);
}
}
- /**
- * Fix additionalProperties incorrectly set to {"type": "null"} when @Nullable
- * propagates from a Map field to its Object value type (resolved as "any type" = {}).
- *
- * Tracked under swagger-core#5115.
- *
- * @param schema the schema to fix
- */
- public static void fixNullOnlyAdditionalProperties(Schema> schema) {
- if (schema == null) {
- return;
- }
- Object additionalProperties = schema.getAdditionalProperties();
- if (additionalProperties instanceof Schema> addPropSchema) {
- boolean isNullOnlyType = false;
- Set types = addPropSchema.getTypes();
- boolean onlyNullTypeOAS31 = types != null && types.size() == 1 && types.contains("null");
- boolean onlyNullTypeOAS30 = types == null && "null".equals(addPropSchema.getType());
- if (onlyNullTypeOAS31 || onlyNullTypeOAS30) {
- isNullOnlyType = true;
- }
- if (isNullOnlyType && addPropSchema.get$ref() == null
- && addPropSchema.getProperties() == null && addPropSchema.getFormat() == null) {
- addPropSchema.setTypes(null);
- addPropSchema.setType(null);
- }
- }
- if (schema.getProperties() != null) {
- schema.getProperties().values().forEach(SpringDocUtils::fixNullOnlyAdditionalProperties);
- }
- }
-
- /**
- * Removes {@code null}-keyed entries from a schema's properties map (and its nested
- * schemas). When swagger-core resolves a {@code @JsonUnwrapped} member (for example
- * Spring HATEOAS {@code EntityModel.getContent()} with HAL disabled), the unwrapped
- * property schemas may have a {@code null} name and get inserted into the properties map
- * under a {@code null} key. Such a key cannot be serialized by Jackson, which fails the
- * whole OpenAPI document with {@code "Null key for a Map not allowed in JSON"}.
- *
- * @param schema the schema to fix
- */
- public static void removeNullKeyProperties(Schema> schema) {
- if (schema == null || schema.getProperties() == null) {
- return;
- }
- schema.getProperties().keySet().removeIf(Objects::isNull);
- schema.getProperties().values().forEach(SpringDocUtils::removeNullKeyProperties);
- }
-
/**
* Handle schema types.
*
diff --git a/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/kotlin/test/org/springdoc/api/v31/app25/NullKeyController.kt b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/kotlin/test/org/springdoc/api/v31/app25/NullKeyController.kt
index 6419037a9..f273b1e9e 100644
--- a/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/kotlin/test/org/springdoc/api/v31/app25/NullKeyController.kt
+++ b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/kotlin/test/org/springdoc/api/v31/app25/NullKeyController.kt
@@ -8,6 +8,8 @@ import org.springframework.web.bind.annotation.RestController
* Reproduces the swagger-core `@JsonUnwrapped` null-property-key issue: when the unwrapped
* member resolves to a `$ref` (a named component model), swagger-core adds a `null`-keyed
* entry to the enclosing schema properties, which breaks JSON serialization of the document.
+ *
+ * Fixed upstream by PR 5193
*/
@RestController
class NullKeyController {
diff --git a/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/resources/results/3.1.0/app25.json b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/resources/results/3.1.0/app25.json
index 650dcae89..148a3c002 100644
--- a/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/resources/results/3.1.0/app25.json
+++ b/springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/resources/results/3.1.0/app25.json
@@ -34,27 +34,41 @@
},
"components": {
"schemas": {
- "OuterResponse": {
- "type": "object",
- "properties": {
- "contractAddress": {
- "type": "string"
+ "NestedObject" : {
+ "type" : "object",
+ "properties" : {
+ "name" : {
+ "type" : "string"
},
- "chainType": {
- "type": "string",
- "enum": [
- "ETH"
- ]
+ "description" : {
+ "type" : [ "string", "null" ]
+ }
+ },
+ "required" : [ "name" ]
+ },
+ "OuterResponse" : {
+ "type" : "object",
+ "properties" : {
+ "contractAddress" : {
+ "type" : "string"
+ },
+ "chainType" : {
+ "type" : "string",
+ "enum" : [ "ETH" ]
+ },
+ "nested" : {
+ "type" : "null",
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/NestedObject"
+ }, {
+ "type" : "null"
+ } ]
},
- "success": {
- "type": "boolean"
+ "success" : {
+ "type" : "boolean"
}
},
- "required": [
- "chainType",
- "contractAddress",
- "success"
- ]
+ "required" : [ "chainType", "contractAddress", "success" ]
}
}
}