From d64edc01c03718de53535c04e697d3fce2e68dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Fri, 7 Nov 2025 18:59:33 +0100 Subject: [PATCH] Prevent Kotlin Serialization converters side effects This commit updates Kotlin serialization converters to perform an additional check invoking KotlinDetector#hasSerializableAnnotation to decide if the related type should be processed or not. The goal is to prevent in the default arrangement conflicts between general purpose converters like Jackson and Kotlin serialization when both are used. New constructors allowing to specify a custom predicate are also introduced. See gh-35761 --- ...tlinSerializationHttpMessageConverter.java | 51 ++++++---- ...rializationBinaryHttpMessageConverter.java | 25 ++++- ...rializationStringHttpMessageConverter.java | 25 ++++- ...SerializationCborHttpMessageConverter.java | 45 ++++++++- ...SerializationJsonHttpMessageConverter.java | 47 ++++++++-- ...alizationProtobufHttpMessageConverter.java | 45 ++++++++- ...ializationCborHttpMessageConverterTests.kt | 85 +++++++++++++---- ...ializationJsonHttpMessageConverterTests.kt | 93 +++++++++++++++---- ...zationProtobufHttpMessageConverterTests.kt | 91 ++++++++++++++---- ...tResponseBodyMethodProcessorKotlinTests.kt | 4 +- 10 files changed, 421 insertions(+), 90 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/AbstractKotlinSerializationHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/AbstractKotlinSerializationHttpMessageConverter.java index fc387721a7e..21c6e868321 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/AbstractKotlinSerializationHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/AbstractKotlinSerializationHttpMessageConverter.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.lang.reflect.Type; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import kotlin.reflect.KType; import kotlinx.serialization.KSerializer; @@ -27,6 +28,7 @@ import kotlinx.serialization.SerialFormat; import kotlinx.serialization.SerializersKt; import org.jspecify.annotations.Nullable; +import org.springframework.core.KotlinDetector; import org.springframework.core.ResolvableType; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; @@ -38,9 +40,11 @@ import org.springframework.util.ConcurrentReferenceHashMap; * Abstract base class for {@link HttpMessageConverter} implementations that * use Kotlin serialization. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only encodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics level + * since it allows combined usage with other general purpose converters without conflicts. + * Alternative constructors with a {@code Predicate} parameter can be used + * to customize this behavior. * * @author Andreas Ahlenstorf * @author Sebastien Deleuze @@ -58,15 +62,30 @@ public abstract class AbstractKotlinSerializationHttpMessageConverter typePredicate; + /** - * Construct an {@code AbstractKotlinSerializationHttpMessageConverter} with multiple supported media type and - * format. - * @param format the format - * @param supportedMediaTypes the supported media types + * Creates a new instance with the given format and supported mime types + * which only converters types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or + * generics level. */ protected AbstractKotlinSerializationHttpMessageConverter(T format, MediaType... supportedMediaTypes) { super(supportedMediaTypes); + this.typePredicate = KotlinDetector::hasSerializableAnnotation; + this.format = format; + } + + /** + * Creates a new instance with the given format and supported mime types + * which only converts types for which the specified predicate returns + * {@code true}. + * @since 7.0 + */ + protected AbstractKotlinSerializationHttpMessageConverter(T format, Predicate typePredicate, MediaType... supportedMediaTypes) { + super(supportedMediaTypes); + this.typePredicate = typePredicate; this.format = format; } @@ -77,27 +96,27 @@ public abstract class AbstractKotlinSerializationHttpMessageConverter clazz) { - return serializer(ResolvableType.forClass(clazz), null) != null; + ResolvableType type = ResolvableType.forClass(clazz); + if (!this.typePredicate.test(type)) { + return false; + } + return serializer(type, null) != null; } @Override public boolean canRead(ResolvableType type, @Nullable MediaType mediaType) { - if (!ResolvableType.NONE.equals(type) && serializer(type, null) != null) { - return canRead(mediaType); - } - else { + if (!this.typePredicate.test(type) || ResolvableType.NONE.equals(type)) { return false; } + return serializer(type, null) != null && canRead(mediaType); } @Override public boolean canWrite(ResolvableType type, Class clazz, @Nullable MediaType mediaType) { - if (!ResolvableType.NONE.equals(type) && serializer(type, null) != null) { - return canWrite(mediaType); - } - else { + if (!this.typePredicate.test(type) || ResolvableType.NONE.equals(type)) { return false; } + return serializer(type, null) != null && canWrite(mediaType); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/converter/KotlinSerializationBinaryHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/KotlinSerializationBinaryHttpMessageConverter.java index 0f3ccc4d8fa..3b564d17232 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/KotlinSerializationBinaryHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/KotlinSerializationBinaryHttpMessageConverter.java @@ -17,11 +17,13 @@ package org.springframework.http.converter; import java.io.IOException; +import java.util.function.Predicate; import kotlinx.serialization.BinaryFormat; import kotlinx.serialization.KSerializer; import kotlinx.serialization.SerializationException; +import org.springframework.core.ResolvableType; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; @@ -31,9 +33,11 @@ import org.springframework.util.StreamUtils; * Abstract base class for {@link HttpMessageConverter} implementations that * defer to Kotlin {@linkplain BinaryFormat binary serializers}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only encodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics level + * since it allows combined usage with other general purpose converters without conflicts. + * Alternative constructors with a {@code Predicate} parameter can be used + * to customize this behavior. * * @author Andreas Ahlenstorf * @author Sebastien Deleuze @@ -47,12 +51,25 @@ public abstract class KotlinSerializationBinaryHttpMessageConverter { /** - * Construct an {@code KotlinSerializationBinaryHttpMessageConverter} with format and supported media types. + * Creates a new instance with the given format and supported mime types + * which only converters types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or + * generics level. */ protected KotlinSerializationBinaryHttpMessageConverter(T format, MediaType... supportedMediaTypes) { super(format, supportedMediaTypes); } + /** + * Creates a new instance with the given format and supported mime types + * which only converts types for which the specified predicate returns + * {@code true}. + * @since 7.0 + */ + protected KotlinSerializationBinaryHttpMessageConverter(T format, Predicate typePredicate, MediaType... supportedMediaTypes) { + super(format, typePredicate, supportedMediaTypes); + } + @Override protected Object readInternal(KSerializer serializer, T format, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { diff --git a/spring-web/src/main/java/org/springframework/http/converter/KotlinSerializationStringHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/KotlinSerializationStringHttpMessageConverter.java index d0bab0b6cbf..ba19761fc0d 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/KotlinSerializationStringHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/KotlinSerializationStringHttpMessageConverter.java @@ -19,12 +19,14 @@ package org.springframework.http.converter; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.function.Predicate; import kotlinx.serialization.KSerializer; import kotlinx.serialization.SerializationException; import kotlinx.serialization.StringFormat; import org.jspecify.annotations.Nullable; +import org.springframework.core.ResolvableType; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; @@ -34,9 +36,11 @@ import org.springframework.util.StreamUtils; * Abstract base class for {@link HttpMessageConverter} implementations that * defer to Kotlin {@linkplain StringFormat string serializers}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only encodes types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics level + * since it allows combined usage with other general purpose converters without conflicts. + * Alternative constructors with a {@code Predicate} parameter can be used + * to customize this behavior. * * @author Andreas Ahlenstorf * @author Sebastien Deleuze @@ -51,12 +55,25 @@ public abstract class KotlinSerializationStringHttpMessageConverter typePredicate, MediaType... supportedMediaTypes) { + super(format, typePredicate, supportedMediaTypes); + } + @Override protected Object readInternal(KSerializer serializer, T format, HttpInputMessage inputMessage) diff --git a/spring-web/src/main/java/org/springframework/http/converter/cbor/KotlinSerializationCborHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/cbor/KotlinSerializationCborHttpMessageConverter.java index 93bf9e6915d..b401ef1b2af 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/cbor/KotlinSerializationCborHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/cbor/KotlinSerializationCborHttpMessageConverter.java @@ -16,8 +16,11 @@ package org.springframework.http.converter.cbor; +import java.util.function.Predicate; + import kotlinx.serialization.cbor.Cbor; +import org.springframework.core.ResolvableType; import org.springframework.http.MediaType; import org.springframework.http.converter.KotlinSerializationBinaryHttpMessageConverter; @@ -27,20 +30,56 @@ import org.springframework.http.converter.KotlinSerializationBinaryHttpMessageCo * kotlinx.serialization. * It supports {@code application/cbor}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics + * level since it allows combined usage with other general purpose JSON decoders + * like {@link JacksonCborHttpMessageConverter} without conflicts. + * + *

Alternative constructors with a {@code Predicate} + * parameter can be used to customize this behavior. For example, + * {@code new KotlinSerializationCborHttpMessageConverter(type -> true)} will decode all types + * supported by Kotlin Serialization, including unannotated Kotlin enumerations, + * numbers, characters, booleans and strings. * * @author Iain Henderson + * @author Sebastien Deleuze * @since 6.0 */ public class KotlinSerializationCborHttpMessageConverter extends KotlinSerializationBinaryHttpMessageConverter { + + /** + * Construct a new converter using {@link Cbor.Default} instance which + * only converts types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationCborHttpMessageConverter() { this(Cbor.Default); } + /** + * Construct a new converter using {@link Cbor.Default} instance which + * only converts types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationCborHttpMessageConverter(Predicate typePredicate) { + this(Cbor.Default, typePredicate); + } + + /** + * Construct a new converter using the provided {@link Cbor} instance which + * only converts types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationCborHttpMessageConverter(Cbor cbor) { super(cbor, MediaType.APPLICATION_CBOR); } + /** + * Construct a new converter using the provided {@link Cbor} instance which + * only converts types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationCborHttpMessageConverter(Cbor cbor, Predicate typePredicate) { + super(cbor, typePredicate, MediaType.APPLICATION_CBOR); + } } diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverter.java index 5535cf1a346..6fab91a7d6d 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverter.java @@ -16,8 +16,11 @@ package org.springframework.http.converter.json; +import java.util.function.Predicate; + import kotlinx.serialization.json.Json; +import org.springframework.core.ResolvableType; import org.springframework.http.MediaType; import org.springframework.http.converter.KotlinSerializationStringHttpMessageConverter; @@ -28,9 +31,16 @@ import org.springframework.http.converter.KotlinSerializationStringHttpMessageCo * It supports {@code application/json} and {@code application/*+json} with * various character sets, {@code UTF-8} being the default. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics + * level since it allows combined usage with other general purpose JSON decoders + * like {@link JacksonJsonHttpMessageConverter} without conflicts. + * + *

Alternative constructors with a {@code Predicate} + * parameter can be used to customize this behavior. For example, + * {@code new KotlinSerializationJsonHttpMessageConverter(type -> true)} will decode all types + * supported by Kotlin Serialization, including unannotated Kotlin enumerations, + * numbers, characters, booleans and strings. * * @author Andreas Ahlenstorf * @author Sebastien Deleuze @@ -40,17 +50,42 @@ import org.springframework.http.converter.KotlinSerializationStringHttpMessageCo */ public class KotlinSerializationJsonHttpMessageConverter extends KotlinSerializationStringHttpMessageConverter { + private static final MediaType[] DEFAULT_JSON_MIME_TYPES = new MediaType[] { + MediaType.APPLICATION_JSON, new MediaType("application", "*+json") }; + /** - * Construct a new {@code KotlinSerializationJsonHttpMessageConverter} with the default configuration. + * Construct a new converter using {@link Json.Default} instance which + * only converts types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. */ public KotlinSerializationJsonHttpMessageConverter() { this(Json.Default); } /** - * Construct a new {@code KotlinSerializationJsonHttpMessageConverter} with a custom configuration. + * Construct a new converter using {@link Json.Default} instance which + * only converts types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationJsonHttpMessageConverter(Predicate typePredicate) { + this(Json.Default, typePredicate); + } + + /** + * Construct a new converter using the provided {@link Json} instance which + * only converts types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. */ public KotlinSerializationJsonHttpMessageConverter(Json json) { - super(json, MediaType.APPLICATION_JSON, new MediaType("application", "*+json")); + super(json, DEFAULT_JSON_MIME_TYPES); + } + + /** + * Construct a new converter using the provided {@link Json} instance which + * only converts types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationJsonHttpMessageConverter(Json json, Predicate typePredicate) { + super(json, typePredicate, DEFAULT_JSON_MIME_TYPES); } } diff --git a/spring-web/src/main/java/org/springframework/http/converter/protobuf/KotlinSerializationProtobufHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/protobuf/KotlinSerializationProtobufHttpMessageConverter.java index 1fa4bb31bbd..d13c26bd236 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/protobuf/KotlinSerializationProtobufHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/protobuf/KotlinSerializationProtobufHttpMessageConverter.java @@ -16,8 +16,11 @@ package org.springframework.http.converter.protobuf; +import java.util.function.Predicate; + import kotlinx.serialization.protobuf.ProtoBuf; +import org.springframework.core.ResolvableType; import org.springframework.http.MediaType; import org.springframework.http.converter.KotlinSerializationBinaryHttpMessageConverter; @@ -27,23 +30,59 @@ import org.springframework.http.converter.KotlinSerializationBinaryHttpMessageCo * kotlinx.serialization. * It supports {@code application/x-protobuf}, {@code application/octet-stream}, and {@code application/vnd.google.protobuf}. * - *

As of Spring Framework 7.0, - * open polymorphism - * is supported. + *

As of Spring Framework 7.0, by default it only converts types annotated with + * {@link kotlinx.serialization.Serializable @Serializable} at type or generics + * level. + * + *

Alternative constructors with a {@code Predicate} + * parameter can be used to customize this behavior. For example, + * {@code new KotlinSerializationProtobufHttpMessageConverter(type -> true)} will convert all types + * supported by Kotlin Serialization, including unannotated Kotlin enumerations, + * numbers, characters, booleans and strings. * * @author Iain Henderson + * @author Sebstien Deleuze * @since 6.0 */ public class KotlinSerializationProtobufHttpMessageConverter extends KotlinSerializationBinaryHttpMessageConverter { + /** + * Construct a new converter using {@link ProtoBuf.Default} instance which + * only converts types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationProtobufHttpMessageConverter() { this(ProtoBuf.Default); } + /** + * Construct a new converter using {@link ProtoBuf.Default} instance which + * only converts types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationProtobufHttpMessageConverter(Predicate typePredicate) { + this(ProtoBuf.Default, typePredicate); + } + + /** + * Construct a new converter using the provided {@link ProtoBuf} instance which + * only converts types annotated with {@link kotlinx.serialization.Serializable @Serializable} + * at type or generics level. + */ public KotlinSerializationProtobufHttpMessageConverter(ProtoBuf protobuf) { super(protobuf, MediaType.APPLICATION_PROTOBUF, MediaType.APPLICATION_OCTET_STREAM, new MediaType("application", "vnd.google.protobuf")); } + /** + * Construct a new converter using the provided {@link ProtoBuf} instance which + * only converts types for which the specified predicate returns {@code true}. + * @since 7.0 + */ + public KotlinSerializationProtobufHttpMessageConverter(ProtoBuf protobuf, Predicate typePredicate) { + super(protobuf, typePredicate, MediaType.APPLICATION_PROTOBUF, MediaType.APPLICATION_OCTET_STREAM, + new MediaType("application", "vnd.google.protobuf")); + } + } diff --git a/spring-web/src/test/kotlin/org/springframework/http/converter/cbor/KotlinSerializationCborHttpMessageConverterTests.kt b/spring-web/src/test/kotlin/org/springframework/http/converter/cbor/KotlinSerializationCborHttpMessageConverterTests.kt index 14e7a54c26e..8821357eb41 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/converter/cbor/KotlinSerializationCborHttpMessageConverterTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/converter/cbor/KotlinSerializationCborHttpMessageConverterTests.kt @@ -33,6 +33,7 @@ import org.springframework.core.Ordered import org.springframework.core.ResolvableType import org.springframework.http.MediaType import org.springframework.http.converter.HttpMessageNotReadableException +import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessageConverter import org.springframework.web.testfixture.http.MockHttpInputMessage import org.springframework.web.testfixture.http.MockHttpOutputMessage @@ -56,57 +57,107 @@ class KotlinSerializationCborHttpMessageConverterTests { fraction = 42f ) private val serializableBeanArray = arrayOf(serializableBean) - private val serializableBeanArrayBody = Cbor.Default.encodeToByteArray(serializableBeanArray) + private val serializableBeanArrayBody = Cbor.encodeToByteArray(serializableBeanArray) @Test fun canReadCbor() { assertThat(converter.canRead(SerializableBean::class.java, MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canRead(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() - assertThat(converter.canRead(String::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canRead(String::class.java, MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canRead(NotSerializableBean::class.java, MediaType.APPLICATION_CBOR)).isFalse() - assertThat(converter.canRead(Map::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canRead(Map::class.java, MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() - assertThat(converter.canRead(List::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canRead(List::class.java, MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() - assertThat(converter.canRead(Set::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canRead(Set::class.java, MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() - assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() - assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isFalse() + assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isFalse() - assertThat(converter.canRead(resolvableTypeOf(), MediaType.APPLICATION_CBOR)).isTrue() - assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canRead(resolvableTypeOf(), MediaType.APPLICATION_CBOR)).isFalse() + assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canRead(resolvableTypeOf(), MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isFalse() } + @Test + fun canReadCborWithAllTypes() { + val converterWithAllTypes = KotlinSerializationCborHttpMessageConverter { true } + + assertThat(converterWithAllTypes.canRead(SerializableBean::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canRead(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() + assertThat(converterWithAllTypes.canRead(String::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canRead(NotSerializableBean::class.java, MediaType.APPLICATION_CBOR)).isFalse() + + assertThat(converterWithAllTypes.canRead(Map::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canRead(List::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canRead(Set::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() + + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isFalse() + + assertThat(converterWithAllTypes.canRead(resolvableTypeOf(), MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf(), MediaType.APPLICATION_CBOR)).isFalse() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_CBOR)).isFalse() + } + @Test fun canWriteCbor() { assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() - assertThat(converter.canWrite(String::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canWrite(String::class.java, MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canWrite(NotSerializableBean::class.java, MediaType.APPLICATION_CBOR)).isFalse() - assertThat(converter.canWrite(Map::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canWrite(Map::class.java, MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), Map::class.java, MediaType.APPLICATION_CBOR)).isTrue() - assertThat(converter.canWrite(List::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canWrite(List::class.java, MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue() - assertThat(converter.canWrite(Set::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canWrite(Set::class.java, MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), Set::class.java, MediaType.APPLICATION_CBOR)).isTrue() - assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue() - assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_CBOR)).isFalse() + assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse() - assertThat(converter.canWrite(resolvableTypeOf(), Ordered::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converter.canWrite(resolvableTypeOf(), Ordered::class.java, MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canWrite(resolvableTypeOf(), Ordered::class.java, MediaType.APPLICATION_CBOR)).isFalse() } + @Test + fun canWriteCborWithAllTypes() { + val converterWithAllTypes = KotlinSerializationCborHttpMessageConverter { true } + + assertThat(converterWithAllTypes.canWrite(SerializableBean::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() + assertThat(converterWithAllTypes.canWrite(String::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canWrite(NotSerializableBean::class.java, MediaType.APPLICATION_CBOR)).isFalse() + + assertThat(converterWithAllTypes.canWrite(Map::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), Map::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canWrite(List::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canWrite(Set::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), Set::class.java, MediaType.APPLICATION_CBOR)).isTrue() + + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse() + + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf(), Ordered::class.java, MediaType.APPLICATION_CBOR)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf(), Ordered::class.java, MediaType.APPLICATION_CBOR)).isFalse() + } + @Test fun readObject() { - val serializableBeanBody = Cbor.Default.encodeToByteArray(serializableBean) + val serializableBeanBody = Cbor.encodeToByteArray(serializableBean) val inputMessage = MockHttpInputMessage(serializableBeanBody) inputMessage.headers.contentType = MediaType.APPLICATION_CBOR val result = converter.read(SerializableBean::class.java, inputMessage) as SerializableBean diff --git a/spring-web/src/test/kotlin/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverterTests.kt b/spring-web/src/test/kotlin/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverterTests.kt index 5be5c7fb3c8..6d1cde38991 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverterTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverterTests.kt @@ -45,7 +45,6 @@ import kotlin.reflect.typeOf * @author Andreas Ahlenstorf * @author Sebastien Deleuze */ -@Suppress("UsePropertyAccessSyntax") class KotlinSerializationJsonHttpMessageConverterTests { private val converter = KotlinSerializationJsonHttpMessageConverter() @@ -54,22 +53,22 @@ class KotlinSerializationJsonHttpMessageConverterTests { fun canReadJson() { assertThat(converter.canRead(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canRead(SerializableBean::class.java, MediaType.APPLICATION_PDF)).isFalse() - assertThat(converter.canRead(String::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canRead(String::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canRead(NotSerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() - assertThat(converter.canRead(Map::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canRead(Map::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() - assertThat(converter.canRead(List::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canRead(List::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() - assertThat(converter.canRead(Set::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canRead(Set::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() - assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() - assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isFalse() + assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_PDF)).isFalse() - assertThat(converter.canRead(resolvableTypeOf(), MediaType.APPLICATION_JSON)).isTrue() - assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canRead(resolvableTypeOf(), MediaType.APPLICATION_JSON)).isFalse() + assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canRead(resolvableTypeOf(), MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isFalse() @@ -78,25 +77,55 @@ class KotlinSerializationJsonHttpMessageConverterTests { assertThat(converter.canRead(ResolvableType.forType(BigDecimal::class.java), MediaType.APPLICATION_JSON)).isFalse() } + @Test + fun canReadJsonWithAllTypes() { + val converterWithAllTypes = KotlinSerializationJsonHttpMessageConverter { true } + + assertThat(converterWithAllTypes.canRead(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canRead(SerializableBean::class.java, MediaType.APPLICATION_PDF)).isFalse() + assertThat(converterWithAllTypes.canRead(String::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canRead(NotSerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() + + assertThat(converterWithAllTypes.canRead(Map::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canRead(List::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canRead(Set::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() + + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_PDF)).isFalse() + + assertThat(converterWithAllTypes.canRead(resolvableTypeOf(), MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf(), MediaType.APPLICATION_JSON)).isFalse() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isFalse() + + assertThat(converterWithAllTypes.canRead(ResolvableType.forType(ResolvableType.NONE.type), MediaType.APPLICATION_JSON)).isFalse() + + assertThat(converterWithAllTypes.canRead(ResolvableType.forType(BigDecimal::class.java), MediaType.APPLICATION_JSON)).isFalse() + } + @Test fun canWriteJson() { assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_PDF)).isFalse() - assertThat(converter.canWrite(String::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canWrite(String::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(NotSerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() - assertThat(converter.canWrite(Map::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canWrite(Map::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), Map::class.java, MediaType.APPLICATION_JSON)).isTrue() - assertThat(converter.canWrite(List::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canWrite(List::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue() - assertThat(converter.canWrite(Set::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canWrite(Set::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), Set::class.java, MediaType.APPLICATION_JSON)).isTrue() - assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue() - assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse() + assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_PDF)).isFalse() - assertThat(converter.canWrite(resolvableTypeOf(), Ordered::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converter.canWrite(resolvableTypeOf(), Ordered::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(resolvableTypeOf(), OrderedImpl::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(ResolvableType.NONE, SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() @@ -104,6 +133,34 @@ class KotlinSerializationJsonHttpMessageConverterTests { assertThat(converter.canWrite(ResolvableType.forType(BigDecimal::class.java), BigDecimal::class.java, MediaType.APPLICATION_JSON)).isFalse() } + @Test + fun canWriteJsonWithAllTypes() { + val converterWithAllTypes = KotlinSerializationJsonHttpMessageConverter { true } + + assertThat(converterWithAllTypes.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canWrite(SerializableBean::class.java, MediaType.APPLICATION_PDF)).isFalse() + assertThat(converterWithAllTypes.canWrite(String::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canWrite(NotSerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() + + assertThat(converterWithAllTypes.canWrite(Map::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), Map::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canWrite(List::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canWrite(Set::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), Set::class.java, MediaType.APPLICATION_JSON)).isTrue() + + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_PDF)).isFalse() + + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf(), Ordered::class.java, MediaType.APPLICATION_JSON)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf(), OrderedImpl::class.java, MediaType.APPLICATION_JSON)).isFalse() + + assertThat(converterWithAllTypes.canWrite(ResolvableType.NONE, SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() + + assertThat(converterWithAllTypes.canWrite(ResolvableType.forType(BigDecimal::class.java), BigDecimal::class.java, MediaType.APPLICATION_JSON)).isFalse() + } + @Test fun canReadMicroformats() { val jsonSubtype = MediaType("application", "vnd.test-micro-type+json") @@ -364,13 +421,13 @@ class KotlinSerializationJsonHttpMessageConverterTests { @Test fun canReadBigDecimalWithSerializerModule() { - val customConverter = KotlinSerializationJsonHttpMessageConverter(customJson) + val customConverter = KotlinSerializationJsonHttpMessageConverter(customJson) { true } assertThat(customConverter.canRead(BigDecimal::class.java, MediaType.APPLICATION_JSON)).isTrue() } @Test fun canWriteBigDecimalWithSerializerModule() { - val customConverter = KotlinSerializationJsonHttpMessageConverter(customJson) + val customConverter = KotlinSerializationJsonHttpMessageConverter(customJson) { true } assertThat(customConverter.canWrite(BigDecimal::class.java, MediaType.APPLICATION_JSON)).isTrue() } diff --git a/spring-web/src/test/kotlin/org/springframework/http/converter/protobuf/KotlinSerializationProtobufHttpMessageConverterTests.kt b/spring-web/src/test/kotlin/org/springframework/http/converter/protobuf/KotlinSerializationProtobufHttpMessageConverterTests.kt index c01eed6e6c2..cf3c0cd424d 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/converter/protobuf/KotlinSerializationProtobufHttpMessageConverterTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/converter/protobuf/KotlinSerializationProtobufHttpMessageConverterTests.kt @@ -27,6 +27,7 @@ import org.springframework.core.Ordered import org.springframework.core.ResolvableType import org.springframework.http.MediaType import org.springframework.http.converter.HttpMessageNotReadableException +import org.springframework.http.converter.cbor.KotlinSerializationCborHttpMessageConverter import org.springframework.web.testfixture.http.MockHttpInputMessage import org.springframework.web.testfixture.http.MockHttpOutputMessage import java.lang.reflect.ParameterizedType @@ -59,26 +60,27 @@ class KotlinSerializationProtobufHttpMessageConverterTests { fraction = 42f ) private val serializableBeanArray = arrayOf(serializableBean) - private val serializableBeanArrayBody = ProtoBuf.Default.encodeToByteArray(serializableBeanArray) + private val serializableBeanArrayBody = ProtoBuf.encodeToByteArray(serializableBeanArray) + @Test fun canReadProtobuf() { for (mimeType in mediaTypes) { assertThat(converter.canRead(SerializableBean::class.java, mimeType)).isTrue() - assertThat(converter.canRead(String::class.java, mimeType)).isTrue() + assertThat(converter.canRead(String::class.java, mimeType)).isFalse() assertThat(converter.canRead(NotSerializableBean::class.java, mimeType)).isFalse() - assertThat(converter.canRead(Map::class.java, mimeType)).isTrue() + assertThat(converter.canRead(Map::class.java, mimeType)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), mimeType)).isTrue() - assertThat(converter.canRead(List::class.java, mimeType)).isTrue() + assertThat(converter.canRead(List::class.java, mimeType)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), mimeType)).isTrue() - assertThat(converter.canRead(Set::class.java, mimeType)).isTrue() + assertThat(converter.canRead(Set::class.java, mimeType)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), mimeType)).isTrue() - assertThat(converter.canRead(resolvableTypeOf>(), mimeType)).isTrue() - assertThat(converter.canRead(resolvableTypeOf>(),mimeType)).isTrue() + assertThat(converter.canRead(resolvableTypeOf>(), mimeType)).isFalse() + assertThat(converter.canRead(resolvableTypeOf>(),mimeType)).isFalse() - assertThat(converter.canRead(resolvableTypeOf(), mimeType)).isTrue() - assertThat(converter.canRead(resolvableTypeOf>(), mimeType)).isTrue() + assertThat(converter.canRead(resolvableTypeOf(), mimeType)).isFalse() + assertThat(converter.canRead(resolvableTypeOf>(), mimeType)).isFalse() assertThat(converter.canRead(resolvableTypeOf(), mimeType)).isFalse() assertThat(converter.canRead(resolvableTypeOf>(), mimeType)).isFalse() } @@ -86,31 +88,86 @@ class KotlinSerializationProtobufHttpMessageConverterTests { assertThat(converter.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isFalse() } + @Test + fun canReadProtobufForAllTypes() { + val converterWithAllTypes = KotlinSerializationProtobufHttpMessageConverter { true } + + for (mimeType in mediaTypes) { + assertThat(converterWithAllTypes.canRead(SerializableBean::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canRead(String::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canRead(NotSerializableBean::class.java, mimeType)).isFalse() + + assertThat(converterWithAllTypes.canRead(Map::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), mimeType)).isTrue() + assertThat(converterWithAllTypes.canRead(List::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), mimeType)).isTrue() + assertThat(converterWithAllTypes.canRead(Set::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), mimeType)).isTrue() + + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), mimeType)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(),mimeType)).isTrue() + + assertThat(converterWithAllTypes.canRead(resolvableTypeOf(), mimeType)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), mimeType)).isTrue() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf(), mimeType)).isFalse() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), mimeType)).isFalse() + } + assertThat(converterWithAllTypes.canRead(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() + assertThat(converterWithAllTypes.canRead(resolvableTypeOf>(), MediaType.APPLICATION_JSON)).isFalse() + } + @Test fun canWriteProtobuf() { for (mimeType in mediaTypes) { assertThat(converter.canWrite(SerializableBean::class.java, mimeType)).isTrue() - assertThat(converter.canWrite(String::class.java, mimeType)).isTrue() + assertThat(converter.canWrite(String::class.java, mimeType)).isFalse() assertThat(converter.canWrite(NotSerializableBean::class.java, mimeType)).isFalse() - assertThat(converter.canWrite(Map::class.java, mimeType)).isTrue() + assertThat(converter.canWrite(Map::class.java, mimeType)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), Map::class.java, mimeType)).isTrue() - assertThat(converter.canWrite(List::class.java, mimeType)).isTrue() + assertThat(converter.canWrite(List::class.java, mimeType)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, mimeType)).isTrue() - assertThat(converter.canWrite(Set::class.java, mimeType)).isTrue() + assertThat(converter.canWrite(Set::class.java, mimeType)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), Set::class.java, mimeType)).isTrue() - assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, mimeType)).isTrue() - assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, mimeType)).isTrue() + assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, mimeType)).isFalse() + assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, mimeType)).isFalse() - assertThat(converter.canWrite(resolvableTypeOf(), Ordered::class.java, mimeType)).isTrue() - assertThat(converter.canWrite(resolvableTypeOf(), OrderedImpl::class.java, mimeType)).isTrue() + assertThat(converter.canWrite(resolvableTypeOf(), Ordered::class.java, mimeType)).isFalse() + assertThat(converter.canWrite(resolvableTypeOf(), OrderedImpl::class.java, mimeType)).isFalse() } assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse() } + @Test + fun canWriteProtobufForAllTypes() { + val converterWithAllTypes = KotlinSerializationProtobufHttpMessageConverter { true } + + for (mimeType in mediaTypes) { + assertThat(converterWithAllTypes.canWrite(SerializableBean::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canWrite(String::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canWrite(NotSerializableBean::class.java, mimeType)).isFalse() + + assertThat(converterWithAllTypes.canWrite(Map::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), Map::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canWrite(List::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canWrite(Set::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), Set::class.java, mimeType)).isTrue() + + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, mimeType)).isTrue() + + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf(), Ordered::class.java, mimeType)).isTrue() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf(), OrderedImpl::class.java, mimeType)).isTrue() + } + + assertThat(converterWithAllTypes.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() + assertThat(converterWithAllTypes.canWrite(resolvableTypeOf>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse() + } + @Test fun readObject() { val serializableBeanBody = ProtoBuf.Default.encodeToByteArray(serializableBean) diff --git a/spring-webmvc/src/test/kotlin/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorKotlinTests.kt b/spring-webmvc/src/test/kotlin/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorKotlinTests.kt index d81e97844ba..3891e9cf41e 100644 --- a/spring-webmvc/src/test/kotlin/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorKotlinTests.kt +++ b/spring-webmvc/src/test/kotlin/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorKotlinTests.kt @@ -109,7 +109,7 @@ class RequestResponseBodyMethodProcessorKotlinTests { val handlerMethod = HandlerMethod(SampleController(), method) val methodReturnType = handlerMethod.returnType - val converters = listOf(KotlinSerializationJsonHttpMessageConverter()) + val converters = listOf(KotlinSerializationJsonHttpMessageConverter { true }) val processor = RequestResponseBodyMethodProcessor(converters, null, listOf(KotlinResponseBodyAdvice())) val returnValue: Any = SampleController().writeNullableMap() @@ -179,7 +179,7 @@ class RequestResponseBodyMethodProcessorKotlinTests { this.servletRequest.setContent(content.toByteArray(StandardCharsets.UTF_8)) this.servletRequest.setContentType("application/json") - val converters = listOf(StringHttpMessageConverter(), KotlinSerializationJsonHttpMessageConverter()) + val converters = listOf(StringHttpMessageConverter(), KotlinSerializationJsonHttpMessageConverter { true }) val processor = RequestResponseBodyMethodProcessor(converters, null, listOf(KotlinRequestBodyAdvice())) val method = SampleController::readNullableMap::javaMethod.get()!!