Skip to content

Commit 2d53570

Browse files
committed
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
1 parent be5eb70 commit 2d53570

File tree

6 files changed

+46
-15
lines changed

6 files changed

+46
-15
lines changed

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import kotlinx.serialization.KSerializer;
2323
import kotlinx.serialization.SerializersKt;
24+
import kotlinx.serialization.descriptors.PolymorphicKind;
2425
import kotlinx.serialization.json.Json;
2526
import org.reactivestreams.Publisher;
2627
import reactor.core.publisher.Flux;
@@ -39,7 +40,9 @@
3940
* Decode a byte stream into JSON and convert to Object's with
4041
* <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>.
4142
*
42-
* <p>This decoder can be used to bind {@code @Serializable} Kotlin classes.
43+
* <p>This decoder can be used to bind {@code @Serializable} Kotlin classes,
44+
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphic serialization</a>
45+
* is not supported.
4346
* It supports {@code application/json} and {@code application/*+json} with
4447
* various character sets, {@code UTF-8} being the default.
4548
*
@@ -132,6 +135,9 @@ private KSerializer<Object> serializer(Type type) {
132135
KSerializer<Object> serializer = serializerCache.get(type);
133136
if (serializer == null) {
134137
serializer = SerializersKt.serializer(type);
138+
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
139+
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
140+
}
135141
serializerCache.put(type, serializer);
136142
}
137143
return serializer;

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import kotlinx.serialization.KSerializer;
2424
import kotlinx.serialization.SerializersKt;
25+
import kotlinx.serialization.descriptors.PolymorphicKind;
2526
import kotlinx.serialization.json.Json;
2627
import org.reactivestreams.Publisher;
2728
import reactor.core.publisher.Flux;
@@ -42,7 +43,9 @@
4243
* Encode from an {@code Object} stream to a byte stream of JSON objects using
4344
* <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>.
4445
*
45-
* <p>This encoder can be used to bind {@code @Serializable} Kotlin classes.
46+
* <p>This encoder can be used to bind {@code @Serializable} Kotlin classes,
47+
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphic serialization</a>
48+
* is not supported.
4649
* It supports {@code application/json} and {@code application/*+json} with
4750
* various character sets, {@code UTF-8} being the default.
4851
*
@@ -120,6 +123,9 @@ private KSerializer<Object> serializer(Type type) {
120123
KSerializer<Object> serializer = serializerCache.get(type);
121124
if (serializer == null) {
122125
serializer = SerializersKt.serializer(type);
126+
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
127+
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
128+
}
123129
serializerCache.put(type, serializer);
124130
}
125131
return serializer;

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import kotlinx.serialization.KSerializer;
2626
import kotlinx.serialization.SerializationException;
2727
import kotlinx.serialization.SerializersKt;
28+
import kotlinx.serialization.descriptors.PolymorphicKind;
2829
import kotlinx.serialization.json.Json;
2930

3031
import org.springframework.core.GenericTypeResolver;
@@ -43,7 +44,9 @@
4344
* that can read and write JSON using
4445
* <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>.
4546
*
46-
* <p>This converter can be used to bind {@code @Serializable} Kotlin classes.
47+
* <p>This converter can be used to bind {@code @Serializable} Kotlin classes,
48+
* <a href="https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#open-polymorphism">open polymorphic serialization</a>
49+
* is not supported.
4750
* It supports {@code application/json} and {@code application/*+json} with
4851
* various character sets, {@code UTF-8} being the default.
4952
*
@@ -182,6 +185,9 @@ private KSerializer<Object> serializer(Type type) {
182185
KSerializer<Object> serializer = serializerCache.get(type);
183186
if (serializer == null) {
184187
serializer = SerializersKt.serializer(type);
188+
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
189+
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
190+
}
185191
serializerCache.put(type, serializer);
186192
}
187193
return serializer;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package org.springframework.http.codec.json
1919
import kotlinx.serialization.Serializable
2020
import org.assertj.core.api.Assertions
2121
import org.junit.jupiter.api.Test
22+
import org.springframework.core.Ordered
2223
import org.springframework.core.ResolvableType
2324
import org.springframework.core.io.buffer.DataBuffer
2425
import org.springframework.core.testfixture.codec.AbstractDecoderTests
@@ -58,6 +59,7 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests<KotlinSerializa
5859
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(List::class.java, Pojo::class.java), MediaType.APPLICATION_JSON)).isTrue()
5960
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(ArrayList::class.java, Int::class.java), MediaType.APPLICATION_JSON)).isTrue()
6061
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(ArrayList::class.java, Int::class.java), MediaType.APPLICATION_PDF)).isFalse()
62+
Assertions.assertThat(decoder.canDecode(ResolvableType.forClass(Ordered::class.java), MediaType.APPLICATION_JSON)).isFalse()
6163
}
6264

6365
@Test

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package org.springframework.http.codec.json
1919
import kotlinx.serialization.Serializable
2020
import org.assertj.core.api.Assertions
2121
import org.junit.jupiter.api.Test
22+
import org.springframework.core.Ordered
2223
import org.springframework.core.ResolvableType
2324
import org.springframework.core.io.buffer.DataBuffer
2425
import org.springframework.core.io.buffer.DataBufferUtils
@@ -91,6 +92,7 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests<KotlinSerializa
9192
Assertions.assertThat(encoder.canEncode(ResolvableType.forClass(Pojo::class.java), MediaType.APPLICATION_XML)).isFalse()
9293
val sseType = ResolvableType.forClass(ServerSentEvent::class.java)
9394
Assertions.assertThat(encoder.canEncode(sseType, MediaType.APPLICATION_JSON)).isFalse()
95+
Assertions.assertThat(encoder.canEncode(ResolvableType.forClass(Ordered::class.java), MediaType.APPLICATION_JSON)).isFalse()
9496
}
9597

9698

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

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import kotlinx.serialization.Serializable
2020
import org.assertj.core.api.Assertions.assertThat
2121
import org.assertj.core.api.Assertions.assertThatExceptionOfType
2222
import org.junit.jupiter.api.Test
23+
import org.springframework.core.Ordered
2324
import org.springframework.http.MediaType
2425
import org.springframework.http.MockHttpInputMessage
2526
import org.springframework.http.MockHttpOutputMessage
@@ -48,14 +49,18 @@ class KotlinSerializationJsonHttpMessageConverterTests {
4849
assertThat(converter.canRead(String::class.java, MediaType.APPLICATION_JSON)).isTrue()
4950
assertThat(converter.canRead(NotSerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse()
5051

51-
assertThat(converter.canRead(Map::class.java, MediaType.APPLICATION_JSON)).isTrue()
52-
assertThat(converter.canRead(List::class.java, MediaType.APPLICATION_JSON)).isTrue()
53-
assertThat(converter.canRead(Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
52+
assertThat(converter.canRead(Map::class.java, MediaType.APPLICATION_JSON)).isFalse()
53+
assertThat(converter.canRead(typeTokenOf<Map<String, SerializableBean>>(), Map::class.java, MediaType.APPLICATION_JSON)).isTrue()
54+
assertThat(converter.canRead(List::class.java, MediaType.APPLICATION_JSON)).isFalse()
55+
assertThat(converter.canRead(typeTokenOf<List<SerializableBean>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
56+
assertThat(converter.canRead(Set::class.java, MediaType.APPLICATION_JSON)).isFalse()
57+
assertThat(converter.canRead(typeTokenOf<Set<SerializableBean>>(), Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
5458

55-
assertThat(converter.canRead(typeTokenOf<List<Int>>(), null, MediaType.APPLICATION_JSON)).isTrue()
56-
assertThat(converter.canRead(typeTokenOf<List<SerializableBean>>(), null, MediaType.APPLICATION_JSON)).isTrue()
57-
assertThat(converter.canRead(typeTokenOf<ArrayList<Int>>(), null, MediaType.APPLICATION_JSON)).isTrue()
58-
assertThat(converter.canRead(typeTokenOf<List<Int>>(), null, MediaType.APPLICATION_PDF)).isFalse()
59+
assertThat(converter.canRead(typeTokenOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
60+
assertThat(converter.canRead(typeTokenOf<ArrayList<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
61+
assertThat(converter.canRead(typeTokenOf<List<Int>>(), List::class.java, MediaType.APPLICATION_PDF)).isFalse()
62+
63+
assertThat(converter.canRead(typeTokenOf<Ordered>(), Ordered::class.java, MediaType.APPLICATION_JSON)).isFalse()
5964
}
6065

6166
@Test
@@ -65,14 +70,18 @@ class KotlinSerializationJsonHttpMessageConverterTests {
6570
assertThat(converter.canWrite(String::class.java, MediaType.APPLICATION_JSON)).isTrue()
6671
assertThat(converter.canWrite(NotSerializableBean::class.java, MediaType.APPLICATION_JSON)).isFalse()
6772

68-
assertThat(converter.canWrite(Map::class.java, MediaType.APPLICATION_JSON)).isTrue()
69-
assertThat(converter.canWrite(List::class.java, MediaType.APPLICATION_JSON)).isTrue()
70-
assertThat(converter.canWrite(Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
73+
assertThat(converter.canWrite(Map::class.java, MediaType.APPLICATION_JSON)).isFalse()
74+
assertThat(converter.canWrite(typeTokenOf<Map<String, SerializableBean>>(), Map::class.java, MediaType.APPLICATION_JSON)).isTrue()
75+
assertThat(converter.canWrite(List::class.java, MediaType.APPLICATION_JSON)).isFalse()
76+
assertThat(converter.canWrite(typeTokenOf<List<SerializableBean>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
77+
assertThat(converter.canWrite(Set::class.java, MediaType.APPLICATION_JSON)).isFalse()
78+
assertThat(converter.canWrite(typeTokenOf<Set<SerializableBean>>(), Set::class.java, MediaType.APPLICATION_JSON)).isTrue()
7179

7280
assertThat(converter.canWrite(typeTokenOf<List<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
73-
assertThat(converter.canWrite(typeTokenOf<List<SerializableBean>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
7481
assertThat(converter.canWrite(typeTokenOf<ArrayList<Int>>(), List::class.java, MediaType.APPLICATION_JSON)).isTrue()
7582
assertThat(converter.canWrite(typeTokenOf<List<Int>>(), List::class.java, MediaType.APPLICATION_PDF)).isFalse()
83+
84+
assertThat(converter.canWrite(typeTokenOf<Ordered>(), Ordered::class.java, MediaType.APPLICATION_JSON)).isFalse()
7685
}
7786

7887
@Test

0 commit comments

Comments
 (0)