diff --git a/CHANGELOG.md b/CHANGELOG.md
index 63d6679d6..247823694 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- #3328, #3337 – `/v3/api-docs` fails with a `NullPointerException` when spring-hateoas is on the classpath without `HateoasProperties`
+- #3314 – `Json Processing Exception occurred` is logged for every constrained parameter whose schema is not a `JsonSchema`
- #3320 – `@Order` and `Ordered` ignored when applying customizers
- #3319 – A `Page` nested in another schema is not replaced by `PagedModel`
- #3313 – Springdoc auto-configurations rely on unspecified auto-configuration ordering
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SchemaTypeMixin.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SchemaTypeMixin.java
index 72a494d21..1be746c2b 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SchemaTypeMixin.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SchemaTypeMixin.java
@@ -30,15 +30,20 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import io.swagger.v3.core.jackson.mixin.Schema31Mixin;
import org.springdoc.core.deserializers.TypeSetDeserializer;
/**
* The type Schema type mixin. Makes the OpenAPI 3.1 {@code type} readable back into the
* {@code types} set, whichever of the two serialized forms it takes.
+ *
+ * It extends the swagger-core mixin so that it can be registered on {@code Schema} itself
+ * without losing the serialization it declares, which is what makes the deserializer apply
+ * to every schema subclass rather than to {@code JsonSchema} alone.
*
* @author Mattias-Sehlstedt
*/
-public interface SchemaTypeMixin {
+public abstract class SchemaTypeMixin extends Schema31Mixin {
/**
* Sets types.
@@ -47,6 +52,6 @@ public interface SchemaTypeMixin {
*/
@JsonProperty("type")
@JsonDeserialize(using = TypeSetDeserializer.class)
- void setTypes(Set types);
+ public abstract void setTypes(Set types);
}
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin31.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin31.java
index 7014b4727..02adfb4bd 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin31.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/mixins/SortedSchemaMixin31.java
@@ -36,8 +36,10 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.core.jackson.mixin.Schema31Mixin;
+import org.springdoc.core.deserializers.TypeSetDeserializer;
/**
* The interface Sorted schema mixin 31.
@@ -121,6 +123,15 @@ public interface SortedSchemaMixin31 {
@JsonSerialize(using = Schema31Mixin.TypeSerializer.class)
Set getTypes();
+ /**
+ * Sets types.
+ *
+ * @param types the types
+ */
+ @JsonProperty("type")
+ @JsonDeserialize(using = TypeSetDeserializer.class)
+ void setTypes(Set types);
+
/**
* Add extension.
*
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ObjectMapperProvider.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ObjectMapperProvider.java
index a2a498b60..a403719b3 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ObjectMapperProvider.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/providers/ObjectMapperProvider.java
@@ -35,7 +35,6 @@
import io.swagger.v3.core.util.Yaml;
import io.swagger.v3.core.util.Yaml31;
import io.swagger.v3.oas.models.OpenAPI;
-import io.swagger.v3.oas.models.media.JsonSchema;
import io.swagger.v3.oas.models.media.Schema;
import org.springdoc.core.mixins.SchemaTypeMixin;
import org.springdoc.core.mixins.SortedOpenAPIMixin;
@@ -76,7 +75,7 @@ public ObjectMapperProvider(SpringDocConfigProperties springDocConfigProperties)
if (openApiVersion == OpenApiVersion.OPENAPI_3_1) {
jsonMapper = Json31.mapper();
yamlMapper = Yaml31.mapper();
- jsonMapper.addMixIn(JsonSchema.class, SchemaTypeMixin.class);
+ jsonMapper.addMixIn(Schema.class, SchemaTypeMixin.class);
if (springDocConfigProperties.isUseArbitrarySchemas()) {
System.setProperty(Schema.USE_ARBITRARY_SCHEMA_PROPERTY, "true");
}
@@ -99,8 +98,10 @@ public ObjectMapperProvider(SpringDocConfigProperties springDocConfigProperties)
public static ObjectMapper createJson(SpringDocConfigProperties springDocConfigProperties) {
OpenApiVersion openApiVersion = springDocConfigProperties.getApiDocs().getVersion();
ObjectMapper objectMapper;
- if (openApiVersion == OpenApiVersion.OPENAPI_3_1)
+ if (openApiVersion == OpenApiVersion.OPENAPI_3_1) {
objectMapper = ObjectMapperFactory.createJson31();
+ objectMapper.addMixIn(Schema.class, SchemaTypeMixin.class);
+ }
else
objectMapper = ObjectMapperFactory.createJson();
diff --git a/springdoc-openapi-starter-common/src/test/java/org/springdoc/core/utils/SpringDocUtilsTest.java b/springdoc-openapi-starter-common/src/test/java/org/springdoc/core/utils/SpringDocUtilsTest.java
index eeb0de530..5c683eba5 100644
--- a/springdoc-openapi-starter-common/src/test/java/org/springdoc/core/utils/SpringDocUtilsTest.java
+++ b/springdoc-openapi-starter-common/src/test/java/org/springdoc/core/utils/SpringDocUtilsTest.java
@@ -26,7 +26,9 @@
import java.util.Set;
+import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.JsonSchema;
+import io.swagger.v3.oas.models.media.StringSchema;
import org.junit.jupiter.api.Test;
import org.springdoc.core.properties.SpringDocConfigProperties;
import org.springdoc.core.properties.SpringDocConfigProperties.ApiDocs.OpenApiVersion;
@@ -90,10 +92,61 @@ void typeArrayIsRetainedForJsonSchemaJsonCloning() {
assertEquals(Set.of("integer", "null"), cloned.getTypes());
}
+ @Test
+ void singleTypeForSchemaSubclassJsonCloning() {
+ ObjectMapperProvider provider = openapi31Provider();
+
+ ArraySchema arraySchema = new ArraySchema();
+ arraySchema.setTypes(Set.of("array"));
+ arraySchema.setItems(new StringSchema());
+
+ ArraySchema cloned = SpringDocUtils.cloneViaJson(arraySchema, ArraySchema.class, provider.jsonMapper());
+
+ // The object is cloned properly, we do not get the type cast fallback
+ assertNotSame(arraySchema, cloned);
+ assertNotNull(cloned);
+ assertEquals(Set.of("array"), cloned.getTypes());
+ }
+
+ @Test
+ void singleTypeForSchemaSubclassJsonCloningWithAStandaloneMapper() {
+ ArraySchema arraySchema = new ArraySchema();
+ arraySchema.setTypes(Set.of("array"));
+ arraySchema.setItems(new StringSchema());
+
+ ArraySchema cloned = SpringDocUtils.cloneViaJson(arraySchema, ArraySchema.class, ObjectMapperProvider.createJson(openapi31Properties()));
+
+ // The object is cloned properly, we do not get the type cast fallback
+ assertNotSame(arraySchema, cloned);
+ assertNotNull(cloned);
+ assertEquals(Set.of("array"), cloned.getTypes());
+ }
+
+ @Test
+ void singleTypeForSchemaSubclassJsonCloningWithASortingMapper() {
+ SpringDocConfigProperties properties = openapi31Properties();
+ properties.setWriterWithOrderByKeys(true);
+
+ ArraySchema arraySchema = new ArraySchema();
+ arraySchema.setTypes(Set.of("array"));
+ arraySchema.setItems(new StringSchema());
+
+ ArraySchema cloned = SpringDocUtils.cloneViaJson(arraySchema, ArraySchema.class, ObjectMapperProvider.createJson(properties));
+
+ // The object is cloned properly, we do not get the type cast fallback
+ assertNotSame(arraySchema, cloned);
+ assertNotNull(cloned);
+ assertEquals(Set.of("array"), cloned.getTypes());
+ }
+
private ObjectMapperProvider openapi31Provider() {
+ return new ObjectMapperProvider(openapi31Properties());
+ }
+
+ private SpringDocConfigProperties openapi31Properties() {
SpringDocConfigProperties properties = new SpringDocConfigProperties();
properties.getApiDocs().setVersion(OpenApiVersion.OPENAPI_3_1);
- return new ObjectMapperProvider(properties);
+ return properties;
}
}