Skip to content

Commit 58cdf71

Browse files
committed
Restrict Kotlin serialization when alternative is available
Previously, Kotlin Serialization would be used too aggressively then an alternative JSON converter was available. This could lead to unwanted results when a response should have been serialized using Jackson, for example, rather than Kotlin Serialization. This commit addresses this by only allowing Kotlin Serialization to serialize types that are not annotated with @serializable when no alternative JSON converter is available. Fixes gh-48070
1 parent 2e52c3c commit 58cdf71

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/KotlinSerializationHttpMessageConvertersConfiguration.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
import kotlinx.serialization.Serializable;
2020
import kotlinx.serialization.json.Json;
2121

22+
import org.springframework.beans.factory.ObjectProvider;
2223
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2324
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2425
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2526
import org.springframework.context.annotation.Bean;
2627
import org.springframework.context.annotation.Configuration;
28+
import org.springframework.http.MediaType;
29+
import org.springframework.http.converter.HttpMessageConverter;
2730
import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessageConverter;
2831

2932
/**
@@ -39,8 +42,23 @@ class KotlinSerializationHttpMessageConvertersConfiguration {
3942

4043
@Bean
4144
@ConditionalOnMissingBean
42-
KotlinSerializationJsonHttpMessageConverter kotlinSerializationJsonHttpMessageConverter(Json json) {
43-
return new KotlinSerializationJsonHttpMessageConverter(json);
45+
KotlinSerializationJsonHttpMessageConverter kotlinSerializationJsonHttpMessageConverter(Json json,
46+
ObjectProvider<HttpMessageConverter<?>> converters) {
47+
return supportsApplicationJson(converters) ? new KotlinSerializationJsonHttpMessageConverter(json)
48+
: new KotlinSerializationJsonHttpMessageConverter(json, (type) -> true);
49+
}
50+
51+
private boolean supportsApplicationJson(ObjectProvider<HttpMessageConverter<?>> converters) {
52+
return converters.orderedStream().filter(this::supportsApplicationJson).findFirst().isPresent();
53+
}
54+
55+
private boolean supportsApplicationJson(HttpMessageConverter<?> converter) {
56+
for (MediaType mediaType : converter.getSupportedMediaTypes()) {
57+
if (!mediaType.equals(MediaType.ALL) && mediaType.isCompatibleWith(MediaType.APPLICATION_JSON)) {
58+
return true;
59+
}
60+
}
61+
return false;
4462
}
4563

4664
}

module/spring-boot-http-converter/src/test/java/org/springframework/boot/http/converter/autoconfigure/HttpMessageConvertersAutoConfigurationTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
4848
import org.springframework.hateoas.RepresentationModel;
4949
import org.springframework.hateoas.server.mvc.TypeConstrainedJacksonJsonHttpMessageConverter;
50+
import org.springframework.http.MediaType;
5051
import org.springframework.http.converter.HttpMessageConverter;
5152
import org.springframework.http.converter.HttpMessageConverters;
5253
import org.springframework.http.converter.HttpMessageConverters.ClientBuilder;
@@ -229,6 +230,24 @@ void kotlinSerializationOrderedAheadOfJsonConverter() {
229230
});
230231
}
231232

233+
@Test
234+
void kotlinSerializationUsesLimitedPredicateWhenOtherJsonConverterIsAvailable() {
235+
allOptionsRunner().run((context) -> {
236+
KotlinSerializationJsonHttpMessageConverter converter = context
237+
.getBean(KotlinSerializationJsonHttpMessageConverter.class);
238+
assertThat(converter.canWrite(Map.class, MediaType.APPLICATION_JSON)).isFalse();
239+
});
240+
}
241+
242+
@Test
243+
void kotlinSerializationUsesUnrestrictedPredicateWhenNoOtherJsonConverterIsAvailable() {
244+
this.contextRunner.withBean(Json.class, () -> Json.Default).run((context) -> {
245+
KotlinSerializationJsonHttpMessageConverter converter = context
246+
.getBean(KotlinSerializationJsonHttpMessageConverter.class);
247+
assertThat(converter.canWrite(Map.class, MediaType.APPLICATION_JSON)).isTrue();
248+
});
249+
}
250+
232251
@Test
233252
void stringDefaultConverter() {
234253
this.contextRunner.run(assertConverter(StringHttpMessageConverter.class, "stringHttpMessageConverter"));

0 commit comments

Comments
 (0)