From fc0d72a8aee2918b38d47c5baac9e88b2cb7c9a8 Mon Sep 17 00:00:00 2001 From: Illia Ovchynnikov Date: Wed, 19 Feb 2025 11:07:16 +0100 Subject: [PATCH 1/2] Check both SerDe bean definitions for @JsonUnwrapped/@Schema --- .../converters/PolymorphicModelConverter.java | 119 +++++++++++++++--- .../springdoc/api/v30/app242/RootModel.java | 41 ++++++ .../api/v30/app242/SpringDocApp242Test.java | 34 +++++ .../api/v30/app242/TestController.java | 15 +++ .../api/v30/app242/UnwrappedModelOne.java | 14 +++ .../api/v30/app242/UnwrappedModelTwo.java | 14 +++ .../springdoc/api/v31/app242/RootModel.java | 41 ++++++ .../api/v31/app242/SpringDocApp242Test.java | 34 +++++ .../api/v31/app242/TestController.java | 15 +++ .../api/v31/app242/UnwrappedModelOne.java | 14 +++ .../api/v31/app242/UnwrappedModelTwo.java | 14 +++ .../test/resources/results/3.0.1/app242.json | 56 +++++++++ .../test/resources/results/3.1.0/app242.json | 56 +++++++++ 13 files changed, 450 insertions(+), 17 deletions(-) create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/RootModel.java create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/SpringDocApp242Test.java create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/TestController.java create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/UnwrappedModelOne.java create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/UnwrappedModelTwo.java create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/RootModel.java create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/SpringDocApp242Test.java create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/TestController.java create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/UnwrappedModelOne.java create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/UnwrappedModelTwo.java create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app242.json create mode 100644 springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.1.0/app242.json diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java index 9a940d349..8832027b7 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java @@ -26,7 +26,7 @@ package org.springdoc.core.converters; -import java.lang.reflect.Field; +import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; @@ -34,10 +34,10 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import com.fasterxml.jackson.annotation.JsonUnwrapped; -import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import io.swagger.v3.core.converter.AnnotatedType; @@ -50,9 +50,11 @@ import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.Schema; import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.reflect.FieldUtils; import org.springdoc.core.providers.ObjectMapperProvider; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; + /** * The type Polymorphic model converter. * @@ -122,28 +124,18 @@ else if (resolvedSchema.getProperties().containsKey(javaType.getRawClass().getSi public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator chain) { JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType()); if (javaType != null) { - BeanDescription javaTypeIntrospection = springDocObjectMapper.jsonMapper().getDeserializationConfig().introspect(javaType); - for (BeanPropertyDefinition property : javaTypeIntrospection.findProperties()) { - boolean isUnwrapped = (property.getField() != null && property.getField().hasAnnotation(JsonUnwrapped.class)) || - (property.getGetter() != null && property.getGetter().hasAnnotation(JsonUnwrapped.class)); - - if (isUnwrapped) { + for (BeanPropertyBiDefinition propertyDef : introspectBeanProperties(javaType)) { + if (propertyDef.isAnyAnnotated(JsonUnwrapped.class)) { if (!TypeNameResolver.std.getUseFqn()) PARENT_TYPES_TO_IGNORE.add(javaType.getRawClass().getSimpleName()); else PARENT_TYPES_TO_IGNORE.add(javaType.getRawClass().getName()); } else { - io.swagger.v3.oas.annotations.media.Schema declaredSchema = null; - if (property.getField() != null) { - declaredSchema = property.getField().getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class); - } else if (property.getGetter() != null) { - declaredSchema = property.getGetter().getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class); - } - + io.swagger.v3.oas.annotations.media.Schema declaredSchema = propertyDef.getAnyAnnotation(io.swagger.v3.oas.annotations.media.Schema.class); if (declaredSchema != null && (ArrayUtils.isNotEmpty(declaredSchema.oneOf()) || ArrayUtils.isNotEmpty(declaredSchema.allOf()))) { - TYPES_TO_SKIP.add(property.getPrimaryType().getRawClass().getSimpleName()); + TYPES_TO_SKIP.add(propertyDef.getPrimaryType().getRawClass().getSimpleName()); } } } @@ -227,4 +219,97 @@ private boolean isConcreteClass(AnnotatedType type) { Class clazz = javaType.getRawClass(); return !Modifier.isAbstract(clazz.getModifiers()) && !clazz.isInterface(); } + + /** + * Introspects the properties of the given Java type based on serialization and deserialization configurations. + * This method identifies properties present in both JSON serialization and deserialization views, + * and pairs them into a list of {@code BeanPropertyBiDefinition}. + */ + private List introspectBeanProperties(JavaType javaType) { + Map forSerializationProps = + springDocObjectMapper.jsonMapper() + .getSerializationConfig() + .introspect(javaType) + .findProperties() + .stream() + .collect(toMap(BeanPropertyDefinition::getName, identity())); + Map forDeserializationProps = + springDocObjectMapper.jsonMapper() + .getDeserializationConfig() + .introspect(javaType) + .findProperties() + .stream() + .collect(toMap(BeanPropertyDefinition::getName, identity())); + + return forSerializationProps.keySet().stream() + .map(key -> new BeanPropertyBiDefinition(forSerializationProps.get(key), forDeserializationProps.get(key))) + .toList(); + } + + /** + * A record representing the bi-definition of a bean property, combining both + * serialization and deserialization property views. + */ + private record BeanPropertyBiDefinition(BeanPropertyDefinition forSerialization, + BeanPropertyDefinition forDeserialization) { + + /** + * Retrieves an annotation of the specified type from either the serialization or + * deserialization property definition (field, getter, setter), returning the first available match. + */ + public A getAnyAnnotation(Class acls) { + A anyForSerializationAnnotation = getAnyAnnotation(forSerialization, acls); + A anyForDeserializationAnnotation = getAnyAnnotation(forDeserialization, acls); + + return anyForSerializationAnnotation != null ? anyForSerializationAnnotation : anyForDeserializationAnnotation; + } + + /** + * Checks if any annotation of the specified type exists across serialization + * or deserialization property definitions. + */ + public boolean isAnyAnnotated(Class acls) { + return getAnyAnnotation(acls) != null; + } + + /** + * Type determined from the primary member for the property being built. + */ + public JavaType getPrimaryType() { + JavaType forSerializationType = null; + if (forSerialization != null) { + forSerializationType = forSerialization.getPrimaryType(); + } + + JavaType forDeserializationType = null; + if (forDeserialization != null) { + forDeserializationType = forDeserialization.getPrimaryType(); + } + + if (forSerializationType != null && forDeserializationType != null && forSerializationType != forDeserializationType) { + throw new IllegalStateException("The property " + forSerialization.getName() + " has different types for serialization and deserialization: " + + forSerializationType + " and " + forDeserializationType); + } + + return forSerializationType != null ? forSerializationType : forDeserializationType; + } + + private A getAnyAnnotation(BeanPropertyDefinition prop, Class acls) { + if (prop == null) { + return null; + } + + if (prop.getField() != null) { + return prop.getField().getAnnotation(acls); + } + if (prop.getGetter() != null) { + return prop.getGetter().getAnnotation(acls); + } + if (prop.getSetter() != null) { + return prop.getSetter().getAnnotation(acls); + } + + return null; + } + } } diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/RootModel.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/RootModel.java new file mode 100644 index 000000000..46db80488 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/RootModel.java @@ -0,0 +1,41 @@ +package test.org.springdoc.api.v30.app242; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +public class RootModel { + + private Integer rootProperty; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonUnwrapped + private UnwrappedModelOne unwrappedModelOne; + + private UnwrappedModelTwo unwrappedModelTwo; + + public Integer getRootProperty() { + return rootProperty; + } + + public void setRootProperty(Integer rootProperty) { + this.rootProperty = rootProperty; + } + + public UnwrappedModelOne getUnwrappedModelOne() { + return unwrappedModelOne; + } + + public void setUnwrappedModelOne(UnwrappedModelOne unwrappedModelOne) { + this.unwrappedModelOne = unwrappedModelOne; + } + + public UnwrappedModelTwo getUnwrappedModelTwo() { + return unwrappedModelTwo; + } + + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @JsonUnwrapped + public void setUnwrappedModelTwo(UnwrappedModelTwo unwrappedModelTwo) { + this.unwrappedModelTwo = unwrappedModelTwo; + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/SpringDocApp242Test.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/SpringDocApp242Test.java new file mode 100644 index 000000000..deacc6533 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/SpringDocApp242Test.java @@ -0,0 +1,34 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2024 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 test.org.springdoc.api.v30.app242; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +public class SpringDocApp242Test extends AbstractSpringDocV30Test { + + @SpringBootApplication + static class SpringDocTestApp {} +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/TestController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/TestController.java new file mode 100644 index 000000000..a55bbfb38 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/TestController.java @@ -0,0 +1,15 @@ +package test.org.springdoc.api.v30.app242; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api") +public class TestController { + + @GetMapping + public RootModel getRootModel() { + return new RootModel(); + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/UnwrappedModelOne.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/UnwrappedModelOne.java new file mode 100644 index 000000000..d263ca0b0 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/UnwrappedModelOne.java @@ -0,0 +1,14 @@ +package test.org.springdoc.api.v30.app242; + +public class UnwrappedModelOne { + + private Integer unwrappedOneProperty; + + public Integer getUnwrappedOneProperty() { + return unwrappedOneProperty; + } + + public void setUnwrappedOneProperty(Integer unwrappedOneProperty) { + this.unwrappedOneProperty = unwrappedOneProperty; + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/UnwrappedModelTwo.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/UnwrappedModelTwo.java new file mode 100644 index 000000000..4899e33a8 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app242/UnwrappedModelTwo.java @@ -0,0 +1,14 @@ +package test.org.springdoc.api.v30.app242; + +public class UnwrappedModelTwo { + + private Integer unwrappedTwoProperty; + + public Integer getUnwrappedTwoProperty() { + return unwrappedTwoProperty; + } + + public void setUnwrappedTwoProperty(Integer unwrappedTwoProperty) { + this.unwrappedTwoProperty = unwrappedTwoProperty; + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/RootModel.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/RootModel.java new file mode 100644 index 000000000..d6d63eee9 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/RootModel.java @@ -0,0 +1,41 @@ +package test.org.springdoc.api.v31.app242; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +public class RootModel { + + private Integer rootProperty; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonUnwrapped + private UnwrappedModelOne unwrappedModelOne; + + private UnwrappedModelTwo unwrappedModelTwo; + + public Integer getRootProperty() { + return rootProperty; + } + + public void setRootProperty(Integer rootProperty) { + this.rootProperty = rootProperty; + } + + public UnwrappedModelOne getUnwrappedModelOne() { + return unwrappedModelOne; + } + + public void setUnwrappedModelOne(UnwrappedModelOne unwrappedModelOne) { + this.unwrappedModelOne = unwrappedModelOne; + } + + public UnwrappedModelTwo getUnwrappedModelTwo() { + return unwrappedModelTwo; + } + + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @JsonUnwrapped + public void setUnwrappedModelTwo(UnwrappedModelTwo unwrappedModelTwo) { + this.unwrappedModelTwo = unwrappedModelTwo; + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/SpringDocApp242Test.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/SpringDocApp242Test.java new file mode 100644 index 000000000..f3646899c --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/SpringDocApp242Test.java @@ -0,0 +1,34 @@ +/* + * + * * + * * * + * * * * + * * * * * Copyright 2019-2024 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 test.org.springdoc.api.v31.app242; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +public class SpringDocApp242Test extends AbstractSpringDocV30Test { + + @SpringBootApplication + static class SpringDocTestApp {} +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/TestController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/TestController.java new file mode 100644 index 000000000..afabf9601 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/TestController.java @@ -0,0 +1,15 @@ +package test.org.springdoc.api.v31.app242; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api") +public class TestController { + + @GetMapping + public RootModel getRootModel() { + return new RootModel(); + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/UnwrappedModelOne.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/UnwrappedModelOne.java new file mode 100644 index 000000000..75d6b82e6 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/UnwrappedModelOne.java @@ -0,0 +1,14 @@ +package test.org.springdoc.api.v31.app242; + +public class UnwrappedModelOne { + + private Integer unwrappedOneProperty; + + public Integer getUnwrappedOneProperty() { + return unwrappedOneProperty; + } + + public void setUnwrappedOneProperty(Integer unwrappedOneProperty) { + this.unwrappedOneProperty = unwrappedOneProperty; + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/UnwrappedModelTwo.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/UnwrappedModelTwo.java new file mode 100644 index 000000000..f892dc776 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app242/UnwrappedModelTwo.java @@ -0,0 +1,14 @@ +package test.org.springdoc.api.v31.app242; + +public class UnwrappedModelTwo { + + private Integer unwrappedTwoProperty; + + public Integer getUnwrappedTwoProperty() { + return unwrappedTwoProperty; + } + + public void setUnwrappedTwoProperty(Integer unwrappedTwoProperty) { + this.unwrappedTwoProperty = unwrappedTwoProperty; + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app242.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app242.json new file mode 100644 index 000000000..2fcfa9cab --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app242.json @@ -0,0 +1,56 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/api": { + "get": { + "tags": [ + "test-controller" + ], + "operationId": "getRootModel", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/RootModel" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "RootModel": { + "type": "object", + "properties": { + "rootProperty": { + "type": "integer", + "format": "int32" + }, + "unwrappedOneProperty": { + "type": "integer", + "format": "int32" + }, + "unwrappedTwoProperty": { + "type": "integer", + "format": "int32" + } + } + } + } + } +} diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.1.0/app242.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.1.0/app242.json new file mode 100644 index 000000000..91ab35ea8 --- /dev/null +++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.1.0/app242.json @@ -0,0 +1,56 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/api": { + "get": { + "tags": [ + "test-controller" + ], + "operationId": "getRootModel", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/RootModel" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "RootModel": { + "type": "object", + "properties": { + "rootProperty": { + "type": "integer", + "format": "int32" + }, + "unwrappedOneProperty": { + "type": "integer", + "format": "int32" + }, + "unwrappedTwoProperty": { + "type": "integer", + "format": "int32" + } + } + } + } + } +} From 6e434674285d370cb5199444235d4ff865938d8d Mon Sep 17 00:00:00 2001 From: Illia Ovchynnikov Date: Wed, 19 Feb 2025 11:29:20 +0100 Subject: [PATCH 2/2] format tabs --- .../converters/PolymorphicModelConverter.java | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java index 8832027b7..a1a8b814a 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java @@ -227,31 +227,33 @@ private boolean isConcreteClass(AnnotatedType type) { */ private List introspectBeanProperties(JavaType javaType) { Map forSerializationProps = - springDocObjectMapper.jsonMapper() - .getSerializationConfig() - .introspect(javaType) - .findProperties() - .stream() - .collect(toMap(BeanPropertyDefinition::getName, identity())); + springDocObjectMapper.jsonMapper() + .getSerializationConfig() + .introspect(javaType) + .findProperties() + .stream() + .collect(toMap(BeanPropertyDefinition::getName, identity())); Map forDeserializationProps = - springDocObjectMapper.jsonMapper() - .getDeserializationConfig() - .introspect(javaType) - .findProperties() - .stream() - .collect(toMap(BeanPropertyDefinition::getName, identity())); + springDocObjectMapper.jsonMapper() + .getDeserializationConfig() + .introspect(javaType) + .findProperties() + .stream() + .collect(toMap(BeanPropertyDefinition::getName, identity())); return forSerializationProps.keySet().stream() - .map(key -> new BeanPropertyBiDefinition(forSerializationProps.get(key), forDeserializationProps.get(key))) - .toList(); + .map(key -> new BeanPropertyBiDefinition(forSerializationProps.get(key), forDeserializationProps.get(key))) + .toList(); } /** * A record representing the bi-definition of a bean property, combining both * serialization and deserialization property views. */ - private record BeanPropertyBiDefinition(BeanPropertyDefinition forSerialization, - BeanPropertyDefinition forDeserialization) { + private record BeanPropertyBiDefinition( + BeanPropertyDefinition forSerialization, + BeanPropertyDefinition forDeserialization + ) { /** * Retrieves an annotation of the specified type from either the serialization or @@ -278,17 +280,17 @@ public boolean isAnyAnnotated(Class acls) { public JavaType getPrimaryType() { JavaType forSerializationType = null; if (forSerialization != null) { - forSerializationType = forSerialization.getPrimaryType(); - } + forSerializationType = forSerialization.getPrimaryType(); + } JavaType forDeserializationType = null; if (forDeserialization != null) { - forDeserializationType = forDeserialization.getPrimaryType(); - } + forDeserializationType = forDeserialization.getPrimaryType(); + } if (forSerializationType != null && forDeserializationType != null && forSerializationType != forDeserializationType) { throw new IllegalStateException("The property " + forSerialization.getName() + " has different types for serialization and deserialization: " - + forSerializationType + " and " + forDeserializationType); + + forSerializationType + " and " + forDeserializationType); } return forSerializationType != null ? forSerializationType : forDeserializationType; @@ -296,18 +298,18 @@ public JavaType getPrimaryType() { private A getAnyAnnotation(BeanPropertyDefinition prop, Class acls) { if (prop == null) { - return null; - } + return null; + } if (prop.getField() != null) { - return prop.getField().getAnnotation(acls); - } + return prop.getField().getAnnotation(acls); + } if (prop.getGetter() != null) { - return prop.getGetter().getAnnotation(acls); - } + return prop.getGetter().getAnnotation(acls); + } if (prop.getSetter() != null) { - return prop.getSetter().getAnnotation(acls); - } + return prop.getSetter().getAnnotation(acls); + } return null; }