mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Configure custom Kotlin Serialization converter
Prior to this commit, `HttpMessageConverters` would consider the Kotlin Serialization JSON converter as an alternative to the Jackson variant. As seen in related issues, this converter is more commonly used for annotated classes specifically and applications often rely on Jackson acting as a fallback for types not supported by Kotlin Serialization. This commit enables applications to configure such a converter on `HttpMessageConverters` and order it ahead of of the JSON one. Closes gh-35733
This commit is contained in:
+21
-20
@@ -31,6 +31,7 @@ import org.springframework.http.converter.cbor.JacksonCborHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessageConverter;
|
||||
import org.springframework.http.converter.protobuf.KotlinSerializationProtobufHttpMessageConverter;
|
||||
import org.springframework.http.converter.smile.JacksonSmileHttpMessageConverter;
|
||||
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
|
||||
@@ -63,42 +64,42 @@ class DefaultHttpMessageConvertersTests {
|
||||
void failsWhenStringConverterDoesNotSupportMediaType() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> HttpMessageConverters.forClient().withStringConverter(new CustomHttpMessageConverter()).build())
|
||||
.withMessage("stringMessageConverter should support 'text/plain'");
|
||||
.withMessage("stringConverter should support 'text/plain'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void failsWhenJsonConverterDoesNotSupportMediaType() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> HttpMessageConverters.forClient().withJsonConverter(new CustomHttpMessageConverter()).build())
|
||||
.withMessage("jsonMessageConverter should support 'application/json'");
|
||||
.withMessage("jsonConverter should support 'application/json'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void failsWhenXmlConverterDoesNotSupportMediaType() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> HttpMessageConverters.forClient().withXmlConverter(new CustomHttpMessageConverter()).build())
|
||||
.withMessage("xmlMessageConverter should support 'text/xml'");
|
||||
.withMessage("xmlConverter should support 'text/xml'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void failsWhenSmileConverterDoesNotSupportMediaType() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> HttpMessageConverters.forClient().withSmileConverter(new CustomHttpMessageConverter()).build())
|
||||
.withMessage("smileMessageConverter should support 'application/x-jackson-smile'");
|
||||
.withMessage("smileConverter should support 'application/x-jackson-smile'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void failsWhenCborConverterDoesNotSupportMediaType() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> HttpMessageConverters.forClient().withCborConverter(new CustomHttpMessageConverter()).build())
|
||||
.withMessage("cborMessageConverter should support 'application/cbor'");
|
||||
.withMessage("cborConverter should support 'application/cbor'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void failsWhenYamlConverterDoesNotSupportMediaType() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> HttpMessageConverters.forClient().withYamlConverter(new CustomHttpMessageConverter()).build())
|
||||
.withMessage("yamlMessageConverter should support 'application/yaml'");
|
||||
.withMessage("yamlConverter should support 'application/yaml'");
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +111,7 @@ class DefaultHttpMessageConvertersTests {
|
||||
var converters = HttpMessageConverters.forClient().registerDefaults().build();
|
||||
assertThat(converters).hasExactlyElementsOfTypes(ByteArrayHttpMessageConverter.class,
|
||||
StringHttpMessageConverter.class, ResourceHttpMessageConverter.class,
|
||||
AllEncompassingFormHttpMessageConverter.class,
|
||||
AllEncompassingFormHttpMessageConverter.class, KotlinSerializationJsonHttpMessageConverter.class,
|
||||
JacksonJsonHttpMessageConverter.class, JacksonSmileHttpMessageConverter.class,
|
||||
JacksonCborHttpMessageConverter.class, JacksonYamlHttpMessageConverter.class,
|
||||
JacksonXmlHttpMessageConverter.class, KotlinSerializationProtobufHttpMessageConverter.class,
|
||||
@@ -124,11 +125,11 @@ class DefaultHttpMessageConvertersTests {
|
||||
|
||||
assertThat(multipartConverter.getPartConverters()).hasExactlyElementsOfTypes(
|
||||
ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class,
|
||||
ResourceHttpMessageConverter.class, JacksonJsonHttpMessageConverter.class,
|
||||
JacksonSmileHttpMessageConverter.class, JacksonCborHttpMessageConverter.class,
|
||||
JacksonYamlHttpMessageConverter.class, JacksonXmlHttpMessageConverter.class,
|
||||
KotlinSerializationProtobufHttpMessageConverter.class, AtomFeedHttpMessageConverter.class,
|
||||
RssChannelHttpMessageConverter.class);
|
||||
ResourceHttpMessageConverter.class, KotlinSerializationJsonHttpMessageConverter.class,
|
||||
JacksonJsonHttpMessageConverter.class, JacksonSmileHttpMessageConverter.class,
|
||||
JacksonCborHttpMessageConverter.class, JacksonYamlHttpMessageConverter.class,
|
||||
JacksonXmlHttpMessageConverter.class, KotlinSerializationProtobufHttpMessageConverter.class,
|
||||
AtomFeedHttpMessageConverter.class, RssChannelHttpMessageConverter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -145,7 +146,7 @@ class DefaultHttpMessageConvertersTests {
|
||||
assertThat(converters).hasExactlyElementsOfTypes(
|
||||
CustomHttpMessageConverter.class, ByteArrayHttpMessageConverter.class,
|
||||
StringHttpMessageConverter.class, ResourceHttpMessageConverter.class,
|
||||
AllEncompassingFormHttpMessageConverter.class,
|
||||
AllEncompassingFormHttpMessageConverter.class, KotlinSerializationJsonHttpMessageConverter.class,
|
||||
JacksonJsonHttpMessageConverter.class, JacksonSmileHttpMessageConverter.class,
|
||||
JacksonCborHttpMessageConverter.class, JacksonYamlHttpMessageConverter.class,
|
||||
JacksonXmlHttpMessageConverter.class, KotlinSerializationProtobufHttpMessageConverter.class,
|
||||
@@ -213,7 +214,7 @@ class DefaultHttpMessageConvertersTests {
|
||||
assertThat(converters).hasExactlyElementsOfTypes(
|
||||
ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class,
|
||||
ResourceHttpMessageConverter.class, ResourceRegionHttpMessageConverter.class,
|
||||
AllEncompassingFormHttpMessageConverter.class,
|
||||
AllEncompassingFormHttpMessageConverter.class, KotlinSerializationJsonHttpMessageConverter.class,
|
||||
JacksonJsonHttpMessageConverter.class, JacksonSmileHttpMessageConverter.class,
|
||||
JacksonCborHttpMessageConverter.class, JacksonYamlHttpMessageConverter.class,
|
||||
JacksonXmlHttpMessageConverter.class, KotlinSerializationProtobufHttpMessageConverter.class,
|
||||
@@ -227,11 +228,11 @@ class DefaultHttpMessageConvertersTests {
|
||||
|
||||
assertThat(multipartConverter.getPartConverters()).hasExactlyElementsOfTypes(
|
||||
ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class,
|
||||
ResourceHttpMessageConverter.class, JacksonJsonHttpMessageConverter.class,
|
||||
JacksonSmileHttpMessageConverter.class, JacksonCborHttpMessageConverter.class,
|
||||
JacksonYamlHttpMessageConverter.class, JacksonXmlHttpMessageConverter.class,
|
||||
KotlinSerializationProtobufHttpMessageConverter.class, AtomFeedHttpMessageConverter.class,
|
||||
RssChannelHttpMessageConverter.class);
|
||||
ResourceHttpMessageConverter.class, KotlinSerializationJsonHttpMessageConverter.class,
|
||||
JacksonJsonHttpMessageConverter.class, JacksonSmileHttpMessageConverter.class,
|
||||
JacksonCborHttpMessageConverter.class, JacksonYamlHttpMessageConverter.class,
|
||||
JacksonXmlHttpMessageConverter.class, KotlinSerializationProtobufHttpMessageConverter.class,
|
||||
AtomFeedHttpMessageConverter.class, RssChannelHttpMessageConverter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -249,7 +250,7 @@ class DefaultHttpMessageConvertersTests {
|
||||
CustomHttpMessageConverter.class,
|
||||
ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class,
|
||||
ResourceHttpMessageConverter.class, ResourceRegionHttpMessageConverter.class,
|
||||
AllEncompassingFormHttpMessageConverter.class,
|
||||
AllEncompassingFormHttpMessageConverter.class, KotlinSerializationJsonHttpMessageConverter.class,
|
||||
JacksonJsonHttpMessageConverter.class, JacksonSmileHttpMessageConverter.class,
|
||||
JacksonCborHttpMessageConverter.class, JacksonYamlHttpMessageConverter.class,
|
||||
JacksonXmlHttpMessageConverter.class, KotlinSerializationProtobufHttpMessageConverter.class,
|
||||
|
||||
+6
@@ -605,6 +605,12 @@ class JacksonJsonHttpMessageConverterTests {
|
||||
assertThat(result).contains("\"string\":\"Foo\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void readEmptyAsNull() throws IOException {
|
||||
MockHttpInputMessage inputMessage = new MockHttpInputMessage("null".getBytes(StandardCharsets.UTF_8));
|
||||
inputMessage.getHeaders().setContentType(new MediaType("application", "json"));
|
||||
assertThat(this.converter.read(Map.class, inputMessage)).isNull();
|
||||
}
|
||||
|
||||
|
||||
interface MyInterface {
|
||||
|
||||
-19
@@ -25,7 +25,6 @@ import java.lang.annotation.Target;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
@@ -339,24 +338,6 @@ class RestClientIntegrationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@ParameterizedRestClientTest
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
void retrieveJsonNull(ClientHttpRequestFactory requestFactory) throws IOException {
|
||||
startServer(requestFactory);
|
||||
|
||||
prepareResponse(builder -> builder
|
||||
.code(200)
|
||||
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.body("null"));
|
||||
|
||||
Map result = this.restClient.get()
|
||||
.uri("/null")
|
||||
.retrieve()
|
||||
.body(Map.class);
|
||||
|
||||
assertThat(result).isNull();
|
||||
}
|
||||
|
||||
@ParameterizedRestClientTest
|
||||
void retrieveJsonEmpty(ClientHttpRequestFactory requestFactory) throws IOException {
|
||||
startServer(requestFactory);
|
||||
|
||||
@@ -53,8 +53,6 @@ import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.SmartHttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessageConverter;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
|
||||
@@ -108,18 +106,6 @@ class RestTemplateTests {
|
||||
template.setErrorHandler(errorHandler);
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultMessageConvertersWithoutKotlinSerialization() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
List<HttpMessageConverter<?>> httpMessageConverters = restTemplate.getMessageConverters();
|
||||
assertThat(httpMessageConverters).extracting("class").containsOnlyOnce(
|
||||
JacksonJsonHttpMessageConverter.class
|
||||
);
|
||||
assertThat(httpMessageConverters).extracting("class").doesNotContain(
|
||||
KotlinSerializationJsonHttpMessageConverter.class
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorPreconditions() {
|
||||
assertThatIllegalArgumentException()
|
||||
|
||||
Reference in New Issue
Block a user