Browse Source

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
pull/34993/merge
Sébastien Deleuze 1 month ago
parent
commit
d64edc01c0
  1. 51
      spring-web/src/main/java/org/springframework/http/converter/AbstractKotlinSerializationHttpMessageConverter.java
  2. 25
      spring-web/src/main/java/org/springframework/http/converter/KotlinSerializationBinaryHttpMessageConverter.java
  3. 25
      spring-web/src/main/java/org/springframework/http/converter/KotlinSerializationStringHttpMessageConverter.java
  4. 45
      spring-web/src/main/java/org/springframework/http/converter/cbor/KotlinSerializationCborHttpMessageConverter.java
  5. 47
      spring-web/src/main/java/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverter.java
  6. 45
      spring-web/src/main/java/org/springframework/http/converter/protobuf/KotlinSerializationProtobufHttpMessageConverter.java
  7. 85
      spring-web/src/test/kotlin/org/springframework/http/converter/cbor/KotlinSerializationCborHttpMessageConverterTests.kt
  8. 93
      spring-web/src/test/kotlin/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverterTests.kt
  9. 91
      spring-web/src/test/kotlin/org/springframework/http/converter/protobuf/KotlinSerializationProtobufHttpMessageConverterTests.kt
  10. 4
      spring-webmvc/src/test/kotlin/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorKotlinTests.kt

51
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.lang.reflect.Type;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate;
import kotlin.reflect.KType; import kotlin.reflect.KType;
import kotlinx.serialization.KSerializer; import kotlinx.serialization.KSerializer;
@ -27,6 +28,7 @@ import kotlinx.serialization.SerialFormat;
import kotlinx.serialization.SerializersKt; import kotlinx.serialization.SerializersKt;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.core.KotlinDetector;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage; import org.springframework.http.HttpOutputMessage;
@ -38,9 +40,11 @@ import org.springframework.util.ConcurrentReferenceHashMap;
* Abstract base class for {@link HttpMessageConverter} implementations that * Abstract base class for {@link HttpMessageConverter} implementations that
* use Kotlin serialization. * use Kotlin serialization.
* *
* <p>As of Spring Framework 7.0, * <p>As of Spring Framework 7.0, by default it only encodes types annotated with
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphism</a> * {@link kotlinx.serialization.Serializable @Serializable} at type or generics level
* is supported. * since it allows combined usage with other general purpose converters without conflicts.
* Alternative constructors with a {@code Predicate<ResolvableType>} parameter can be used
* to customize this behavior.
* *
* @author Andreas Ahlenstorf * @author Andreas Ahlenstorf
* @author Sebastien Deleuze * @author Sebastien Deleuze
@ -58,15 +62,30 @@ public abstract class AbstractKotlinSerializationHttpMessageConverter<T extends
private final T format; private final T format;
private final Predicate<ResolvableType> typePredicate;
/** /**
* Construct an {@code AbstractKotlinSerializationHttpMessageConverter} with multiple supported media type and * Creates a new instance with the given format and supported mime types
* format. * which only converters types annotated with
* @param format the format * {@link kotlinx.serialization.Serializable @Serializable} at type or
* @param supportedMediaTypes the supported media types * generics level.
*/ */
protected AbstractKotlinSerializationHttpMessageConverter(T format, MediaType... supportedMediaTypes) { protected AbstractKotlinSerializationHttpMessageConverter(T format, MediaType... supportedMediaTypes) {
super(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<ResolvableType> typePredicate, MediaType... supportedMediaTypes) {
super(supportedMediaTypes);
this.typePredicate = typePredicate;
this.format = format; this.format = format;
} }
@ -77,27 +96,27 @@ public abstract class AbstractKotlinSerializationHttpMessageConverter<T extends
@Override @Override
protected boolean supports(Class<?> clazz) { protected boolean supports(Class<?> 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 @Override
public boolean canRead(ResolvableType type, @Nullable MediaType mediaType) { public boolean canRead(ResolvableType type, @Nullable MediaType mediaType) {
if (!ResolvableType.NONE.equals(type) && serializer(type, null) != null) { if (!this.typePredicate.test(type) || ResolvableType.NONE.equals(type)) {
return canRead(mediaType);
}
else {
return false; return false;
} }
return serializer(type, null) != null && canRead(mediaType);
} }
@Override @Override
public boolean canWrite(ResolvableType type, Class<?> clazz, @Nullable MediaType mediaType) { public boolean canWrite(ResolvableType type, Class<?> clazz, @Nullable MediaType mediaType) {
if (!ResolvableType.NONE.equals(type) && serializer(type, null) != null) { if (!this.typePredicate.test(type) || ResolvableType.NONE.equals(type)) {
return canWrite(mediaType);
}
else {
return false; return false;
} }
return serializer(type, null) != null && canWrite(mediaType);
} }
@Override @Override

25
spring-web/src/main/java/org/springframework/http/converter/KotlinSerializationBinaryHttpMessageConverter.java

@ -17,11 +17,13 @@
package org.springframework.http.converter; package org.springframework.http.converter;
import java.io.IOException; import java.io.IOException;
import java.util.function.Predicate;
import kotlinx.serialization.BinaryFormat; import kotlinx.serialization.BinaryFormat;
import kotlinx.serialization.KSerializer; import kotlinx.serialization.KSerializer;
import kotlinx.serialization.SerializationException; import kotlinx.serialization.SerializationException;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage; import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -31,9 +33,11 @@ import org.springframework.util.StreamUtils;
* Abstract base class for {@link HttpMessageConverter} implementations that * Abstract base class for {@link HttpMessageConverter} implementations that
* defer to Kotlin {@linkplain BinaryFormat binary serializers}. * defer to Kotlin {@linkplain BinaryFormat binary serializers}.
* *
* <p>As of Spring Framework 7.0, * <p>As of Spring Framework 7.0, by default it only encodes types annotated with
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphism</a> * {@link kotlinx.serialization.Serializable @Serializable} at type or generics level
* is supported. * since it allows combined usage with other general purpose converters without conflicts.
* Alternative constructors with a {@code Predicate<ResolvableType>} parameter can be used
* to customize this behavior.
* *
* @author Andreas Ahlenstorf * @author Andreas Ahlenstorf
* @author Sebastien Deleuze * @author Sebastien Deleuze
@ -47,12 +51,25 @@ public abstract class KotlinSerializationBinaryHttpMessageConverter<T extends Bi
extends AbstractKotlinSerializationHttpMessageConverter<T> { extends AbstractKotlinSerializationHttpMessageConverter<T> {
/** /**
* 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) { protected KotlinSerializationBinaryHttpMessageConverter(T format, MediaType... supportedMediaTypes) {
super(format, 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<ResolvableType> typePredicate, MediaType... supportedMediaTypes) {
super(format, typePredicate, supportedMediaTypes);
}
@Override @Override
protected Object readInternal(KSerializer<Object> serializer, T format, HttpInputMessage inputMessage) protected Object readInternal(KSerializer<Object> serializer, T format, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException { throws IOException, HttpMessageNotReadableException {

25
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.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.function.Predicate;
import kotlinx.serialization.KSerializer; import kotlinx.serialization.KSerializer;
import kotlinx.serialization.SerializationException; import kotlinx.serialization.SerializationException;
import kotlinx.serialization.StringFormat; import kotlinx.serialization.StringFormat;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage; import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -34,9 +36,11 @@ import org.springframework.util.StreamUtils;
* Abstract base class for {@link HttpMessageConverter} implementations that * Abstract base class for {@link HttpMessageConverter} implementations that
* defer to Kotlin {@linkplain StringFormat string serializers}. * defer to Kotlin {@linkplain StringFormat string serializers}.
* *
* <p>As of Spring Framework 7.0, * <p>As of Spring Framework 7.0, by default it only encodes types annotated with
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphism</a> * {@link kotlinx.serialization.Serializable @Serializable} at type or generics level
* is supported. * since it allows combined usage with other general purpose converters without conflicts.
* Alternative constructors with a {@code Predicate<ResolvableType>} parameter can be used
* to customize this behavior.
* *
* @author Andreas Ahlenstorf * @author Andreas Ahlenstorf
* @author Sebastien Deleuze * @author Sebastien Deleuze
@ -51,12 +55,25 @@ public abstract class KotlinSerializationStringHttpMessageConverter<T extends St
/** /**
* Construct an {@code KotlinSerializationStringHttpMessageConverter} 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 KotlinSerializationStringHttpMessageConverter(T format, MediaType... supportedMediaTypes) { protected KotlinSerializationStringHttpMessageConverter(T format, MediaType... supportedMediaTypes) {
super(format, 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 KotlinSerializationStringHttpMessageConverter(T format, Predicate<ResolvableType> typePredicate, MediaType... supportedMediaTypes) {
super(format, typePredicate, supportedMediaTypes);
}
@Override @Override
protected Object readInternal(KSerializer<Object> serializer, T format, HttpInputMessage inputMessage) protected Object readInternal(KSerializer<Object> serializer, T format, HttpInputMessage inputMessage)

45
spring-web/src/main/java/org/springframework/http/converter/cbor/KotlinSerializationCborHttpMessageConverter.java

@ -16,8 +16,11 @@
package org.springframework.http.converter.cbor; package org.springframework.http.converter.cbor;
import java.util.function.Predicate;
import kotlinx.serialization.cbor.Cbor; import kotlinx.serialization.cbor.Cbor;
import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.KotlinSerializationBinaryHttpMessageConverter; import org.springframework.http.converter.KotlinSerializationBinaryHttpMessageConverter;
@ -27,20 +30,56 @@ import org.springframework.http.converter.KotlinSerializationBinaryHttpMessageCo
* <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>. * <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>.
* It supports {@code application/cbor}. * It supports {@code application/cbor}.
* *
* <p>As of Spring Framework 7.0, * <p>As of Spring Framework 7.0, by default it only types annotated with
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphism</a> * {@link kotlinx.serialization.Serializable @Serializable} at type or generics
* is supported. * level since it allows combined usage with other general purpose JSON decoders
* like {@link JacksonCborHttpMessageConverter} without conflicts.
*
* <p>Alternative constructors with a {@code Predicate<ResolvableType>}
* 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 Iain Henderson
* @author Sebastien Deleuze
* @since 6.0 * @since 6.0
*/ */
public class KotlinSerializationCborHttpMessageConverter extends KotlinSerializationBinaryHttpMessageConverter<Cbor> { public class KotlinSerializationCborHttpMessageConverter extends KotlinSerializationBinaryHttpMessageConverter<Cbor> {
/**
* 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() { public KotlinSerializationCborHttpMessageConverter() {
this(Cbor.Default); 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<ResolvableType> 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) { public KotlinSerializationCborHttpMessageConverter(Cbor cbor) {
super(cbor, MediaType.APPLICATION_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<ResolvableType> typePredicate) {
super(cbor, typePredicate, MediaType.APPLICATION_CBOR);
}
} }

47
spring-web/src/main/java/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverter.java

@ -16,8 +16,11 @@
package org.springframework.http.converter.json; package org.springframework.http.converter.json;
import java.util.function.Predicate;
import kotlinx.serialization.json.Json; import kotlinx.serialization.json.Json;
import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.KotlinSerializationStringHttpMessageConverter; 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 * It supports {@code application/json} and {@code application/*+json} with
* various character sets, {@code UTF-8} being the default. * various character sets, {@code UTF-8} being the default.
* *
* <p>As of Spring Framework 7.0, * <p>As of Spring Framework 7.0, by default it only types annotated with
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphism</a> * {@link kotlinx.serialization.Serializable @Serializable} at type or generics
* is supported. * level since it allows combined usage with other general purpose JSON decoders
* like {@link JacksonJsonHttpMessageConverter} without conflicts.
*
* <p>Alternative constructors with a {@code Predicate<ResolvableType>}
* 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 Andreas Ahlenstorf
* @author Sebastien Deleuze * @author Sebastien Deleuze
@ -40,17 +50,42 @@ import org.springframework.http.converter.KotlinSerializationStringHttpMessageCo
*/ */
public class KotlinSerializationJsonHttpMessageConverter extends KotlinSerializationStringHttpMessageConverter<Json> { public class KotlinSerializationJsonHttpMessageConverter extends KotlinSerializationStringHttpMessageConverter<Json> {
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() { public KotlinSerializationJsonHttpMessageConverter() {
this(Json.Default); 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<ResolvableType> 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) { 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<ResolvableType> typePredicate) {
super(json, typePredicate, DEFAULT_JSON_MIME_TYPES);
} }
} }

45
spring-web/src/main/java/org/springframework/http/converter/protobuf/KotlinSerializationProtobufHttpMessageConverter.java

@ -16,8 +16,11 @@
package org.springframework.http.converter.protobuf; package org.springframework.http.converter.protobuf;
import java.util.function.Predicate;
import kotlinx.serialization.protobuf.ProtoBuf; import kotlinx.serialization.protobuf.ProtoBuf;
import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.KotlinSerializationBinaryHttpMessageConverter; import org.springframework.http.converter.KotlinSerializationBinaryHttpMessageConverter;
@ -27,23 +30,59 @@ import org.springframework.http.converter.KotlinSerializationBinaryHttpMessageCo
* <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>. * <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>.
* It supports {@code application/x-protobuf}, {@code application/octet-stream}, and {@code application/vnd.google.protobuf}. * It supports {@code application/x-protobuf}, {@code application/octet-stream}, and {@code application/vnd.google.protobuf}.
* *
* <p>As of Spring Framework 7.0, * <p>As of Spring Framework 7.0, by default it only converts types annotated with
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphism</a> * {@link kotlinx.serialization.Serializable @Serializable} at type or generics
* is supported. * level.
*
* <p>Alternative constructors with a {@code Predicate<ResolvableType>}
* 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 Iain Henderson
* @author Sebstien Deleuze
* @since 6.0 * @since 6.0
*/ */
public class KotlinSerializationProtobufHttpMessageConverter extends public class KotlinSerializationProtobufHttpMessageConverter extends
KotlinSerializationBinaryHttpMessageConverter<ProtoBuf> { KotlinSerializationBinaryHttpMessageConverter<ProtoBuf> {
/**
* 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() { public KotlinSerializationProtobufHttpMessageConverter() {
this(ProtoBuf.Default); 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<ResolvableType> 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) { public KotlinSerializationProtobufHttpMessageConverter(ProtoBuf protobuf) {
super(protobuf, MediaType.APPLICATION_PROTOBUF, MediaType.APPLICATION_OCTET_STREAM, super(protobuf, MediaType.APPLICATION_PROTOBUF, MediaType.APPLICATION_OCTET_STREAM,
new MediaType("application", "vnd.google.protobuf")); 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<ResolvableType> typePredicate) {
super(protobuf, typePredicate, MediaType.APPLICATION_PROTOBUF, MediaType.APPLICATION_OCTET_STREAM,
new MediaType("application", "vnd.google.protobuf"));
}
} }

85
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.core.ResolvableType
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.http.converter.HttpMessageNotReadableException 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.MockHttpInputMessage
import org.springframework.web.testfixture.http.MockHttpOutputMessage import org.springframework.web.testfixture.http.MockHttpOutputMessage
@ -56,57 +57,107 @@ class KotlinSerializationCborHttpMessageConverterTests {
fraction = 42f fraction = 42f
) )
private val serializableBeanArray = arrayOf(serializableBean) private val serializableBeanArray = arrayOf(serializableBean)
private val serializableBeanArrayBody = Cbor.Default.encodeToByteArray(serializableBeanArray) private val serializableBeanArrayBody = Cbor.encodeToByteArray(serializableBeanArray)
@Test @Test
fun canReadCbor() { fun canReadCbor() {
assertThat(converter.canRead(SerializableBean::class.java, MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canRead(SerializableBean::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converter.canRead(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() 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(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<Map<String, SerializableBean>>(), MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canRead(resolvableTypeOf<Map<String, SerializableBean>>(), 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<List<SerializableBean>>(), MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canRead(resolvableTypeOf<List<SerializableBean>>(), 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<Set<SerializableBean>>(), MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canRead(resolvableTypeOf<Set<SerializableBean>>(), MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_CBOR)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<ArrayList<Int>>(), MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canRead(resolvableTypeOf<ArrayList<Int>>(), MediaType.APPLICATION_CBOR)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<Ordered>(), MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canRead(resolvableTypeOf<Ordered>(), MediaType.APPLICATION_CBOR)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<List<Ordered>>(), MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canRead(resolvableTypeOf<List<Ordered>>(), MediaType.APPLICATION_CBOR)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<OrderedImpl>(), MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canRead(resolvableTypeOf<OrderedImpl>(), MediaType.APPLICATION_CBOR)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<List<OrderedImpl>>(), MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canRead(resolvableTypeOf<List<OrderedImpl>>(), 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<Map<String, SerializableBean>>(), MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canRead(List::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<SerializableBean>>(), MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canRead(Set::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<Set<SerializableBean>>(), MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<ArrayList<Int>>(), MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_JSON)).isFalse()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<Ordered>(), MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<Ordered>>(), MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<OrderedImpl>(), MediaType.APPLICATION_CBOR)).isFalse()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<OrderedImpl>>(), MediaType.APPLICATION_CBOR)).isFalse()
}
@Test @Test
fun canWriteCbor() { fun canWriteCbor() {
assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() 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(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<String, SerializableBean>>(), Map::class.java, MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<Map<String, SerializableBean>>(), 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<SerializableBean>>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<List<SerializableBean>>(), 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<SerializableBean>>(), Set::class.java, MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<Set<SerializableBean>>(), Set::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_CBOR)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<ArrayList<Int>>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<ArrayList<Int>>(), List::class.java, MediaType.APPLICATION_CBOR)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<Ordered>(), Ordered::class.java, MediaType.APPLICATION_CBOR)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<Ordered>(), Ordered::class.java, MediaType.APPLICATION_CBOR)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<OrderedImpl>(), Ordered::class.java, MediaType.APPLICATION_CBOR)).isFalse() assertThat(converter.canWrite(resolvableTypeOf<OrderedImpl>(), 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<String, SerializableBean>>(), Map::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canWrite(List::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<List<SerializableBean>>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canWrite(Set::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<Set<SerializableBean>>(), Set::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<ArrayList<Int>>(), List::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<Ordered>(), Ordered::class.java, MediaType.APPLICATION_CBOR)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<OrderedImpl>(), Ordered::class.java, MediaType.APPLICATION_CBOR)).isFalse()
}
@Test @Test
fun readObject() { fun readObject() {
val serializableBeanBody = Cbor.Default.encodeToByteArray(serializableBean) val serializableBeanBody = Cbor.encodeToByteArray(serializableBean)
val inputMessage = MockHttpInputMessage(serializableBeanBody) val inputMessage = MockHttpInputMessage(serializableBeanBody)
inputMessage.headers.contentType = MediaType.APPLICATION_CBOR inputMessage.headers.contentType = MediaType.APPLICATION_CBOR
val result = converter.read(SerializableBean::class.java, inputMessage) as SerializableBean val result = converter.read(SerializableBean::class.java, inputMessage) as SerializableBean

93
spring-web/src/test/kotlin/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverterTests.kt

@ -45,7 +45,6 @@ import kotlin.reflect.typeOf
* @author Andreas Ahlenstorf * @author Andreas Ahlenstorf
* @author Sebastien Deleuze * @author Sebastien Deleuze
*/ */
@Suppress("UsePropertyAccessSyntax")
class KotlinSerializationJsonHttpMessageConverterTests { class KotlinSerializationJsonHttpMessageConverterTests {
private val converter = KotlinSerializationJsonHttpMessageConverter() private val converter = KotlinSerializationJsonHttpMessageConverter()
@ -54,22 +53,22 @@ class KotlinSerializationJsonHttpMessageConverterTests {
fun canReadJson() { fun canReadJson() {
assertThat(converter.canRead(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canRead(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(SerializableBean::class.java, MediaType.APPLICATION_PDF)).isFalse() 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(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<Map<String, SerializableBean>>(), MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canRead(resolvableTypeOf<Map<String, SerializableBean>>(), 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<List<SerializableBean>>(), MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canRead(resolvableTypeOf<List<SerializableBean>>(), 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<Set<SerializableBean>>(), MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canRead(resolvableTypeOf<Set<SerializableBean>>(), MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<ArrayList<Int>>(), MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canRead(resolvableTypeOf<ArrayList<Int>>(), MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_PDF)).isFalse() assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_PDF)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<Ordered>(), MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canRead(resolvableTypeOf<Ordered>(), MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<List<Ordered>>(), MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canRead(resolvableTypeOf<List<Ordered>>(), MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<OrderedImpl>(), MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canRead(resolvableTypeOf<OrderedImpl>(), MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<List<OrderedImpl>>(), MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canRead(resolvableTypeOf<List<OrderedImpl>>(), MediaType.APPLICATION_JSON)).isFalse()
@ -78,25 +77,55 @@ class KotlinSerializationJsonHttpMessageConverterTests {
assertThat(converter.canRead(ResolvableType.forType(BigDecimal::class.java), MediaType.APPLICATION_JSON)).isFalse() 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<Map<String, SerializableBean>>(), MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canRead(List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<SerializableBean>>(), MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canRead(Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<Set<SerializableBean>>(), MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<ArrayList<Int>>(), MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_PDF)).isFalse()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<Ordered>(), MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<Ordered>>(), MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<OrderedImpl>(), MediaType.APPLICATION_JSON)).isFalse()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<OrderedImpl>>(), 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 @Test
fun canWriteJson() { fun canWriteJson() {
assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_PDF)).isFalse() 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(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<String, SerializableBean>>(), Map::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<Map<String, SerializableBean>>(), 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<SerializableBean>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<List<SerializableBean>>(), 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<SerializableBean>>(), Set::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<Set<SerializableBean>>(), Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<ArrayList<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<ArrayList<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_PDF)).isFalse() assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_PDF)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<Ordered>(), Ordered::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<Ordered>(), Ordered::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<OrderedImpl>(), OrderedImpl::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(resolvableTypeOf<OrderedImpl>(), OrderedImpl::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canWrite(ResolvableType.NONE, SerializableBean::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() 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<String, SerializableBean>>(), Map::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canWrite(List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<List<SerializableBean>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canWrite(Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<Set<SerializableBean>>(), Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<ArrayList<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_PDF)).isFalse()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<Ordered>(), Ordered::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<OrderedImpl>(), 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 @Test
fun canReadMicroformats() { fun canReadMicroformats() {
val jsonSubtype = MediaType("application", "vnd.test-micro-type+json") val jsonSubtype = MediaType("application", "vnd.test-micro-type+json")
@ -364,13 +421,13 @@ class KotlinSerializationJsonHttpMessageConverterTests {
@Test @Test
fun canReadBigDecimalWithSerializerModule() { fun canReadBigDecimalWithSerializerModule() {
val customConverter = KotlinSerializationJsonHttpMessageConverter(customJson) val customConverter = KotlinSerializationJsonHttpMessageConverter(customJson) { true }
assertThat(customConverter.canRead(BigDecimal::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(customConverter.canRead(BigDecimal::class.java, MediaType.APPLICATION_JSON)).isTrue()
} }
@Test @Test
fun canWriteBigDecimalWithSerializerModule() { fun canWriteBigDecimalWithSerializerModule() {
val customConverter = KotlinSerializationJsonHttpMessageConverter(customJson) val customConverter = KotlinSerializationJsonHttpMessageConverter(customJson) { true }
assertThat(customConverter.canWrite(BigDecimal::class.java, MediaType.APPLICATION_JSON)).isTrue() assertThat(customConverter.canWrite(BigDecimal::class.java, MediaType.APPLICATION_JSON)).isTrue()
} }

91
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.core.ResolvableType
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.http.converter.HttpMessageNotReadableException 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.MockHttpInputMessage
import org.springframework.web.testfixture.http.MockHttpOutputMessage import org.springframework.web.testfixture.http.MockHttpOutputMessage
import java.lang.reflect.ParameterizedType import java.lang.reflect.ParameterizedType
@ -59,26 +60,27 @@ class KotlinSerializationProtobufHttpMessageConverterTests {
fraction = 42f fraction = 42f
) )
private val serializableBeanArray = arrayOf(serializableBean) private val serializableBeanArray = arrayOf(serializableBean)
private val serializableBeanArrayBody = ProtoBuf.Default.encodeToByteArray(serializableBeanArray) private val serializableBeanArrayBody = ProtoBuf.encodeToByteArray(serializableBeanArray)
@Test @Test
fun canReadProtobuf() { fun canReadProtobuf() {
for (mimeType in mediaTypes) { for (mimeType in mediaTypes) {
assertThat(converter.canRead(SerializableBean::class.java, mimeType)).isTrue() 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(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<Map<String, SerializableBean>>(), mimeType)).isTrue() assertThat(converter.canRead(resolvableTypeOf<Map<String, SerializableBean>>(), mimeType)).isTrue()
assertThat(converter.canRead(List::class.java, mimeType)).isTrue() assertThat(converter.canRead(List::class.java, mimeType)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<List<SerializableBean>>(), mimeType)).isTrue() assertThat(converter.canRead(resolvableTypeOf<List<SerializableBean>>(), mimeType)).isTrue()
assertThat(converter.canRead(Set::class.java, mimeType)).isTrue() assertThat(converter.canRead(Set::class.java, mimeType)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<Set<SerializableBean>>(), mimeType)).isTrue() assertThat(converter.canRead(resolvableTypeOf<Set<SerializableBean>>(), mimeType)).isTrue()
assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), mimeType)).isTrue() assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), mimeType)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<ArrayList<Int>>(),mimeType)).isTrue() assertThat(converter.canRead(resolvableTypeOf<ArrayList<Int>>(),mimeType)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<Ordered>(), mimeType)).isTrue() assertThat(converter.canRead(resolvableTypeOf<Ordered>(), mimeType)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<List<Ordered>>(), mimeType)).isTrue() assertThat(converter.canRead(resolvableTypeOf<List<Ordered>>(), mimeType)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<OrderedImpl>(), mimeType)).isFalse() assertThat(converter.canRead(resolvableTypeOf<OrderedImpl>(), mimeType)).isFalse()
assertThat(converter.canRead(resolvableTypeOf<List<OrderedImpl>>(), mimeType)).isFalse() assertThat(converter.canRead(resolvableTypeOf<List<OrderedImpl>>(), mimeType)).isFalse()
} }
@ -86,31 +88,86 @@ class KotlinSerializationProtobufHttpMessageConverterTests {
assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canRead(resolvableTypeOf<List<Int>>(), 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<Map<String, SerializableBean>>(), mimeType)).isTrue()
assertThat(converterWithAllTypes.canRead(List::class.java, mimeType)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<SerializableBean>>(), mimeType)).isTrue()
assertThat(converterWithAllTypes.canRead(Set::class.java, mimeType)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<Set<SerializableBean>>(), mimeType)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<Int>>(), mimeType)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<ArrayList<Int>>(),mimeType)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<Ordered>(), mimeType)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<Ordered>>(), mimeType)).isTrue()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<OrderedImpl>(), mimeType)).isFalse()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<OrderedImpl>>(), mimeType)).isFalse()
}
assertThat(converterWithAllTypes.canRead(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converterWithAllTypes.canRead(resolvableTypeOf<List<Int>>(), MediaType.APPLICATION_JSON)).isFalse()
}
@Test @Test
fun canWriteProtobuf() { fun canWriteProtobuf() {
for (mimeType in mediaTypes) { for (mimeType in mediaTypes) {
assertThat(converter.canWrite(SerializableBean::class.java, mimeType)).isTrue() 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(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<String, SerializableBean>>(), Map::class.java, mimeType)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<Map<String, SerializableBean>>(), 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<SerializableBean>>(), List::class.java, mimeType)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<List<SerializableBean>>(), 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<SerializableBean>>(), Set::class.java, mimeType)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<Set<SerializableBean>>(), Set::class.java, mimeType)).isTrue()
assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, mimeType)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, mimeType)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<ArrayList<Int>>(), List::class.java, mimeType)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<ArrayList<Int>>(), List::class.java, mimeType)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<Ordered>(), Ordered::class.java, mimeType)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<Ordered>(), Ordered::class.java, mimeType)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<Ordered>(), OrderedImpl::class.java, mimeType)).isTrue() assertThat(converter.canWrite(resolvableTypeOf<Ordered>(), OrderedImpl::class.java, mimeType)).isFalse()
} }
assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse() assertThat(converter.canWrite(resolvableTypeOf<List<Int>>(), 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<String, SerializableBean>>(), Map::class.java, mimeType)).isTrue()
assertThat(converterWithAllTypes.canWrite(List::class.java, mimeType)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<List<SerializableBean>>(), List::class.java, mimeType)).isTrue()
assertThat(converterWithAllTypes.canWrite(Set::class.java, mimeType)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<Set<SerializableBean>>(), Set::class.java, mimeType)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, mimeType)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<ArrayList<Int>>(), List::class.java, mimeType)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<Ordered>(), Ordered::class.java, mimeType)).isTrue()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<Ordered>(), OrderedImpl::class.java, mimeType)).isTrue()
}
assertThat(converterWithAllTypes.canWrite(SerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converterWithAllTypes.canWrite(resolvableTypeOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse()
}
@Test @Test
fun readObject() { fun readObject() {
val serializableBeanBody = ProtoBuf.Default.encodeToByteArray(serializableBean) val serializableBeanBody = ProtoBuf.Default.encodeToByteArray(serializableBean)

4
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 handlerMethod = HandlerMethod(SampleController(), method)
val methodReturnType = handlerMethod.returnType val methodReturnType = handlerMethod.returnType
val converters = listOf(KotlinSerializationJsonHttpMessageConverter()) val converters = listOf(KotlinSerializationJsonHttpMessageConverter { true })
val processor = RequestResponseBodyMethodProcessor(converters, null, listOf(KotlinResponseBodyAdvice())) val processor = RequestResponseBodyMethodProcessor(converters, null, listOf(KotlinResponseBodyAdvice()))
val returnValue: Any = SampleController().writeNullableMap() val returnValue: Any = SampleController().writeNullableMap()
@ -179,7 +179,7 @@ class RequestResponseBodyMethodProcessorKotlinTests {
this.servletRequest.setContent(content.toByteArray(StandardCharsets.UTF_8)) this.servletRequest.setContent(content.toByteArray(StandardCharsets.UTF_8))
this.servletRequest.setContentType("application/json") this.servletRequest.setContentType("application/json")
val converters = listOf(StringHttpMessageConverter(), KotlinSerializationJsonHttpMessageConverter()) val converters = listOf(StringHttpMessageConverter(), KotlinSerializationJsonHttpMessageConverter { true })
val processor = RequestResponseBodyMethodProcessor(converters, null, listOf(KotlinRequestBodyAdvice())) val processor = RequestResponseBodyMethodProcessor(converters, null, listOf(KotlinRequestBodyAdvice()))
val method = SampleController::readNullableMap::javaMethod.get()!! val method = SampleController::readNullableMap::javaMethod.get()!!

Loading…
Cancel
Save