Skip to content

Commit

Permalink
Prevent kotlinx.serialization usage on collection of interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
sdeleuze authored and lxbzmy committed Mar 26, 2022
1 parent fcd5af1 commit fb02da2
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -17,11 +17,14 @@
package org.springframework.http.codec.json;

import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import kotlinx.serialization.KSerializer;
import kotlinx.serialization.SerializersKt;
import kotlinx.serialization.descriptors.PolymorphicKind;
import kotlinx.serialization.descriptors.SerialDescriptor;
import kotlinx.serialization.json.Json;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
Expand Down Expand Up @@ -135,12 +138,26 @@ private KSerializer<Object> serializer(Type type) {
KSerializer<Object> serializer = serializerCache.get(type);
if (serializer == null) {
serializer = SerializersKt.serializer(type);
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
if (hasPolymorphism(serializer.getDescriptor(), new HashSet<>())) {
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
}
serializerCache.put(type, serializer);
}
return serializer;
}

private boolean hasPolymorphism(SerialDescriptor descriptor, Set<String> alreadyProcessed) {
alreadyProcessed.add(descriptor.getSerialName());
if (descriptor.getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
return true;
}
for (int i = 0 ; i < descriptor.getElementsCount() ; i++) {
SerialDescriptor elementDescriptor = descriptor.getElementDescriptor(i);
if (!alreadyProcessed.contains(elementDescriptor.getSerialName()) && hasPolymorphism(elementDescriptor, alreadyProcessed)) {
return true;
}
}
return false;
}

}
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -17,12 +17,15 @@
package org.springframework.http.codec.json;

import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import kotlinx.serialization.KSerializer;
import kotlinx.serialization.SerializersKt;
import kotlinx.serialization.descriptors.PolymorphicKind;
import kotlinx.serialization.descriptors.SerialDescriptor;
import kotlinx.serialization.json.Json;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
Expand Down Expand Up @@ -123,12 +126,26 @@ private KSerializer<Object> serializer(Type type) {
KSerializer<Object> serializer = serializerCache.get(type);
if (serializer == null) {
serializer = SerializersKt.serializer(type);
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
if (hasPolymorphism(serializer.getDescriptor(), new HashSet<>())) {
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
}
serializerCache.put(type, serializer);
}
return serializer;
}

private boolean hasPolymorphism(SerialDescriptor descriptor, Set<String> alreadyProcessed) {
alreadyProcessed.add(descriptor.getSerialName());
if (descriptor.getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
return true;
}
for (int i = 0 ; i < descriptor.getElementsCount() ; i++) {
SerialDescriptor elementDescriptor = descriptor.getElementDescriptor(i);
if (!alreadyProcessed.contains(elementDescriptor.getSerialName()) && hasPolymorphism(elementDescriptor, alreadyProcessed)) {
return true;
}
}
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import kotlinx.serialization.KSerializer;
import kotlinx.serialization.SerializationException;
import kotlinx.serialization.SerializersKt;
import kotlinx.serialization.descriptors.PolymorphicKind;
import kotlinx.serialization.descriptors.SerialDescriptor;
import kotlinx.serialization.json.Json;

import org.springframework.core.GenericTypeResolver;
Expand Down Expand Up @@ -185,12 +188,26 @@ private KSerializer<Object> serializer(Type type) {
KSerializer<Object> serializer = serializerCache.get(type);
if (serializer == null) {
serializer = SerializersKt.serializer(type);
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
if (hasPolymorphism(serializer.getDescriptor(), new HashSet<>())) {
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
}
serializerCache.put(type, serializer);
}
return serializer;
}

private boolean hasPolymorphism(SerialDescriptor descriptor, Set<String> alreadyProcessed) {
alreadyProcessed.add(descriptor.getSerialName());
if (descriptor.getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
return true;
}
for (int i = 0 ; i < descriptor.getElementsCount() ; i++) {
SerialDescriptor elementDescriptor = descriptor.getElementDescriptor(i);
if (!alreadyProcessed.contains(elementDescriptor.getSerialName()) && hasPolymorphism(elementDescriptor, alreadyProcessed)) {
return true;
}
}
return false;
}

}
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -56,6 +56,7 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests<KotlinSerializa
MediaType("application", "json", StandardCharsets.ISO_8859_1))).isTrue()

Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(List::class.java, Int::class.java), MediaType.APPLICATION_JSON)).isTrue()
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(List::class.java, Ordered::class.java), MediaType.APPLICATION_JSON)).isFalse()
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()
Expand Down Expand Up @@ -101,6 +102,6 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests<KotlinSerializa


@Serializable
data class Pojo(val foo: String, val bar: String)
data class Pojo(val foo: String, val bar: String, val pojo: Pojo? = null)

}
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -52,7 +52,8 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests<KotlinSerializa
MediaType("application", "json", StandardCharsets.US_ASCII))).isTrue()

Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(List::class.java, Int::class.java), MediaType.APPLICATION_JSON)).isTrue()
Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(List::class.java, KotlinSerializationJsonDecoderTests.Pojo::class.java), MediaType.APPLICATION_JSON)).isTrue()
Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(List::class.java, Ordered::class.java), MediaType.APPLICATION_JSON)).isFalse()
Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(List::class.java, Pojo::class.java), MediaType.APPLICATION_JSON)).isTrue()
Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(ArrayList::class.java, Int::class.java), MediaType.APPLICATION_JSON)).isTrue()
Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(ArrayList::class.java, Int::class.java), MediaType.APPLICATION_PDF)).isFalse()
}
Expand Down Expand Up @@ -97,6 +98,6 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests<KotlinSerializa


@Serializable
data class Pojo(val foo: String, val bar: String)
data class Pojo(val foo: String, val bar: String, val pojo: Pojo? = null)

}
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -61,6 +61,7 @@ class KotlinSerializationJsonHttpMessageConverterTests {
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()
assertThat(converter.canRead(typeTokenOf<List<Ordered>>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse()
}

@Test
Expand Down Expand Up @@ -315,7 +316,8 @@ class KotlinSerializationJsonHttpMessageConverterTests {
val number: Int,
val string: String?,
val bool: Boolean,
val fraction: Float
val fraction: Float,
val serializableBean: SerializableBean? = null
)

data class NotSerializableBean(val string: String)
Expand Down

0 comments on commit fb02da2

Please sign in to comment.