Browse Source

Prevent kotlinx.serialization usage on interfaces

Remove support for open polymorphic serialization in
kotlinx.serialization web converters and codecs in order
to prevent serialization handling suitable for Jackson
or other general purpose Java JSON libraries.

This will probably need further refinements for collections
for example, and could ultimately be fixed when
kotlinx.serialization will provide a dedicated function to
evaluate upfront if a type can be serialized or not.

Closes gh-26298
pull/26385/head
Sébastien Deleuze 5 years ago
parent
commit
2d53570f4c
  1. 8
      spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonDecoder.java
  2. 8
      spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonEncoder.java
  3. 10
      spring-web/src/main/java/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverter.java
  4. 2
      spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonDecoderTests.kt
  5. 2
      spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonEncoderTests.kt
  6. 31
      spring-web/src/test/kotlin/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverterTests.kt

8
spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonDecoder.java

@ -21,6 +21,7 @@ import java.util.Map; @@ -21,6 +21,7 @@ import java.util.Map;
import kotlinx.serialization.KSerializer;
import kotlinx.serialization.SerializersKt;
import kotlinx.serialization.descriptors.PolymorphicKind;
import kotlinx.serialization.json.Json;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
@ -39,7 +40,9 @@ import org.springframework.util.MimeType; @@ -39,7 +40,9 @@ import org.springframework.util.MimeType;
* Decode a byte stream into JSON and convert to Object's with
* <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>.
*
* <p>This decoder can be used to bind {@code @Serializable} Kotlin classes.
* <p>This decoder can be used to bind {@code @Serializable} Kotlin classes,
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphic serialization</a>
* is not supported.
* It supports {@code application/json} and {@code application/*+json} with
* various character sets, {@code UTF-8} being the default.
*
@ -132,6 +135,9 @@ public class KotlinSerializationJsonDecoder extends AbstractDecoder<Object> { @@ -132,6 +135,9 @@ public class KotlinSerializationJsonDecoder extends AbstractDecoder<Object> {
KSerializer<Object> serializer = serializerCache.get(type);
if (serializer == null) {
serializer = SerializersKt.serializer(type);
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
}
serializerCache.put(type, serializer);
}
return serializer;

8
spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonEncoder.java

@ -22,6 +22,7 @@ import java.util.Map; @@ -22,6 +22,7 @@ import java.util.Map;
import kotlinx.serialization.KSerializer;
import kotlinx.serialization.SerializersKt;
import kotlinx.serialization.descriptors.PolymorphicKind;
import kotlinx.serialization.json.Json;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
@ -42,7 +43,9 @@ import org.springframework.util.MimeType; @@ -42,7 +43,9 @@ import org.springframework.util.MimeType;
* Encode from an {@code Object} stream to a byte stream of JSON objects using
* <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>.
*
* <p>This encoder can be used to bind {@code @Serializable} Kotlin classes.
* <p>This encoder can be used to bind {@code @Serializable} Kotlin classes,
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphic serialization</a>
* is not supported.
* It supports {@code application/json} and {@code application/*+json} with
* various character sets, {@code UTF-8} being the default.
*
@ -120,6 +123,9 @@ public class KotlinSerializationJsonEncoder extends AbstractEncoder<Object> { @@ -120,6 +123,9 @@ public class KotlinSerializationJsonEncoder extends AbstractEncoder<Object> {
KSerializer<Object> serializer = serializerCache.get(type);
if (serializer == null) {
serializer = SerializersKt.serializer(type);
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
}
serializerCache.put(type, serializer);
}
return serializer;

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,6 +25,7 @@ import java.util.Map; @@ -25,6 +25,7 @@ import java.util.Map;
import kotlinx.serialization.KSerializer;
import kotlinx.serialization.SerializationException;
import kotlinx.serialization.SerializersKt;
import kotlinx.serialization.descriptors.PolymorphicKind;
import kotlinx.serialization.json.Json;
import org.springframework.core.GenericTypeResolver;
@ -43,7 +44,9 @@ import org.springframework.util.StreamUtils; @@ -43,7 +44,9 @@ import org.springframework.util.StreamUtils;
* that can read and write JSON using
* <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>.
*
* <p>This converter can be used to bind {@code @Serializable} Kotlin classes.
* <p>This converter can be used to bind {@code @Serializable} Kotlin classes,
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphic serialization</a>
* is not supported.
* It supports {@code application/json} and {@code application/*+json} with
* various character sets, {@code UTF-8} being the default.
*
@ -182,6 +185,9 @@ public class KotlinSerializationJsonHttpMessageConverter extends AbstractGeneric @@ -182,6 +185,9 @@ public class KotlinSerializationJsonHttpMessageConverter extends AbstractGeneric
KSerializer<Object> serializer = serializerCache.get(type);
if (serializer == null) {
serializer = SerializersKt.serializer(type);
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
}
serializerCache.put(type, serializer);
}
return serializer;

2
spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonDecoderTests.kt

@ -19,6 +19,7 @@ package org.springframework.http.codec.json @@ -19,6 +19,7 @@ package org.springframework.http.codec.json
import kotlinx.serialization.Serializable
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test
import org.springframework.core.Ordered
import org.springframework.core.ResolvableType
import org.springframework.core.io.buffer.DataBuffer
import org.springframework.core.testfixture.codec.AbstractDecoderTests
@ -58,6 +59,7 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests<KotlinSerializa @@ -58,6 +59,7 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests<KotlinSerializa
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(List::class.java, Pojo::class.java), MediaType.APPLICATION_JSON)).isTrue()
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(ArrayList::class.java, Int::class.java), MediaType.APPLICATION_JSON)).isTrue()
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(ArrayList::class.java, Int::class.java), MediaType.APPLICATION_PDF)).isFalse()
Assertions.assertThat(decoder.canDecode(ResolvableType.forClass(Ordered::class.java), MediaType.APPLICATION_JSON)).isFalse()
}
@Test

2
spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonEncoderTests.kt

@ -19,6 +19,7 @@ package org.springframework.http.codec.json @@ -19,6 +19,7 @@ package org.springframework.http.codec.json
import kotlinx.serialization.Serializable
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test
import org.springframework.core.Ordered
import org.springframework.core.ResolvableType
import org.springframework.core.io.buffer.DataBuffer
import org.springframework.core.io.buffer.DataBufferUtils
@ -91,6 +92,7 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests<KotlinSerializa @@ -91,6 +92,7 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests<KotlinSerializa
Assertions.assertThat(encoder.canEncode(ResolvableType.forClass(Pojo::class.java), MediaType.APPLICATION_XML)).isFalse()
val sseType = ResolvableType.forClass(ServerSentEvent::class.java)
Assertions.assertThat(encoder.canEncode(sseType, MediaType.APPLICATION_JSON)).isFalse()
Assertions.assertThat(encoder.canEncode(ResolvableType.forClass(Ordered::class.java), MediaType.APPLICATION_JSON)).isFalse()
}

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

@ -20,6 +20,7 @@ import kotlinx.serialization.Serializable @@ -20,6 +20,7 @@ import kotlinx.serialization.Serializable
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.jupiter.api.Test
import org.springframework.core.Ordered
import org.springframework.http.MediaType
import org.springframework.http.MockHttpInputMessage
import org.springframework.http.MockHttpOutputMessage
@ -48,14 +49,18 @@ class KotlinSerializationJsonHttpMessageConverterTests { @@ -48,14 +49,18 @@ class KotlinSerializationJsonHttpMessageConverterTests {
assertThat(converter.canRead(String::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(NotSerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canRead(Map::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(Map::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canRead(typeTokenOf<Map<String, SerializableBean>>(), Map::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(List::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canRead(typeTokenOf<List<SerializableBean>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(Set::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canRead(typeTokenOf<Set<SerializableBean>>(), Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(typeTokenOf<List<Int>>(), null, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(typeTokenOf<List<SerializableBean>>(), null, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(typeTokenOf<ArrayList<Int>>(), null, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(typeTokenOf<List<Int>>(), null, MediaType.APPLICATION_PDF)).isFalse()
assertThat(converter.canRead(typeTokenOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(typeTokenOf<ArrayList<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canRead(typeTokenOf<List<Int>>(), List::class.java, MediaType.APPLICATION_PDF)).isFalse()
assertThat(converter.canRead(typeTokenOf<Ordered>(), Ordered::class.java, MediaType.APPLICATION_JSON)).isFalse()
}
@Test
@ -65,14 +70,18 @@ class KotlinSerializationJsonHttpMessageConverterTests { @@ -65,14 +70,18 @@ class KotlinSerializationJsonHttpMessageConverterTests {
assertThat(converter.canWrite(String::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(NotSerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canWrite(Map::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(Map::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canWrite(typeTokenOf<Map<String, SerializableBean>>(), Map::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(List::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canWrite(typeTokenOf<List<SerializableBean>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(Set::class.java, MediaType.APPLICATION_JSON)).isFalse()
assertThat(converter.canWrite(typeTokenOf<Set<SerializableBean>>(), Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(typeTokenOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(typeTokenOf<List<SerializableBean>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(typeTokenOf<ArrayList<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
assertThat(converter.canWrite(typeTokenOf<List<Int>>(), List::class.java, MediaType.APPLICATION_PDF)).isFalse()
assertThat(converter.canWrite(typeTokenOf<Ordered>(), Ordered::class.java, MediaType.APPLICATION_JSON)).isFalse()
}
@Test

Loading…
Cancel
Save