From 6a8f0d6d7d4379860e0197f321f0784f8b46e5df Mon Sep 17 00:00:00 2001 From: Hyoungjune Date: Thu, 29 Feb 2024 16:26:26 +0900 Subject: [PATCH] Add web support for Yaml via Jackson This commit adds support for application/yaml in MediaType and leverages jackson-dataformat-yaml in order to support Yaml in RestTemplate, RestClient and Spring MVC. See gh-32345 --- spring-web/spring-web.gradle | 1 + .../org/springframework/http/MediaType.java | 14 +++- .../json/Jackson2ObjectMapperBuilder.java | 18 +++++ ...ppingJackson2YamlHttpMessageConverter.java | 76 +++++++++++++++++++ .../http/converter/yaml/package-info.java | 9 +++ .../web/client/DefaultRestClientBuilder.java | 8 ++ .../web/client/RestTemplate.java | 9 +++ .../Jackson2ObjectMapperBuilderTests.java | 9 +++ spring-webmvc/spring-webmvc.gradle | 1 + .../AnnotationDrivenBeanDefinitionParser.java | 19 ++++- .../WebMvcConfigurationSupport.java | 15 ++++ 11 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 spring-web/src/main/java/org/springframework/http/converter/yaml/MappingJackson2YamlHttpMessageConverter.java create mode 100644 spring-web/src/main/java/org/springframework/http/converter/yaml/package-info.java diff --git a/spring-web/spring-web.gradle b/spring-web/spring-web.gradle index 4e500e367c..03b4a0ff19 100644 --- a/spring-web/spring-web.gradle +++ b/spring-web/spring-web.gradle @@ -15,6 +15,7 @@ dependencies { optional("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor") optional("com.fasterxml.jackson.dataformat:jackson-dataformat-smile") optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") + optional("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml") optional("com.fasterxml.woodstox:woodstox-core") optional("com.google.code.gson:gson") optional("com.google.protobuf:protobuf-java-util") diff --git a/spring-web/src/main/java/org/springframework/http/MediaType.java b/spring-web/src/main/java/org/springframework/http/MediaType.java index 4760864b24..336b76e94c 100644 --- a/spring-web/src/main/java/org/springframework/http/MediaType.java +++ b/spring-web/src/main/java/org/springframework/http/MediaType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -45,6 +45,7 @@ import org.springframework.util.StringUtils; * @author Sebastien Deleuze * @author Kazuki Shimizu * @author Sam Brannen + * @author Hyoungjune Kim * @since 3.0 * @see * HTTP 1.1: Semantics and Content, section 3.1.1.1 @@ -311,6 +312,16 @@ public class MediaType extends MimeType implements Serializable { */ public static final String APPLICATION_XML_VALUE = "application/xml"; + /** + * Public constant media type for {@code application/yaml}. + */ + public static final MediaType APPLICATION_YAML; + + /** + * A String equivalent of {@link MediaType#APPLICATION_YAML}. + */ + public static final String APPLICATION_YAML_VALE = "application/yaml"; + /** * Public constant media type for {@code image/gif}. */ @@ -454,6 +465,7 @@ public class MediaType extends MimeType implements Serializable { APPLICATION_STREAM_JSON = new MediaType("application", "stream+json"); APPLICATION_XHTML_XML = new MediaType("application", "xhtml+xml"); APPLICATION_XML = new MediaType("application", "xml"); + APPLICATION_YAML = new MediaType("application", "yaml"); IMAGE_GIF = new MediaType("image", "gif"); IMAGE_JPEG = new MediaType("image", "jpeg"); IMAGE_PNG = new MediaType("image", "png"); diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java index fb5b9735db..641ff1d677 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java @@ -56,6 +56,7 @@ import com.fasterxml.jackson.dataformat.smile.SmileFactory; import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule; import com.fasterxml.jackson.dataformat.xml.XmlFactory; import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.springframework.beans.BeanUtils; import org.springframework.context.ApplicationContext; @@ -95,6 +96,7 @@ import org.springframework.util.xml.StaxUtils; * @author Juergen Hoeller * @author Tadaya Tsuyukubo * @author Eddú Meléndez + * @author Hyoungjune Kim * @since 4.1.1 * @see #build() * @see #configure(ObjectMapper) @@ -936,6 +938,15 @@ public class Jackson2ObjectMapperBuilder { return new Jackson2ObjectMapperBuilder().factory(new CborFactoryInitializer().create()); } + /** + * Obtain a {@link Jackson2ObjectMapperBuilder} instance in order to + * build a Yaml data format {@link ObjectMapper} instance. + * @since 6.2 + */ + public static Jackson2ObjectMapperBuilder yaml() { + return new Jackson2ObjectMapperBuilder().factory(new YamlFactoryInitializer().create()); + } + private static class XmlObjectMapperInitializer { @@ -976,4 +987,11 @@ public class Jackson2ObjectMapperBuilder { } } + private static class YamlFactoryInitializer { + + public JsonFactory create() { + return new YAMLFactory(); + } + } + } diff --git a/spring-web/src/main/java/org/springframework/http/converter/yaml/MappingJackson2YamlHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/yaml/MappingJackson2YamlHttpMessageConverter.java new file mode 100644 index 0000000000..38d74a9094 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/converter/yaml/MappingJackson2YamlHttpMessageConverter.java @@ -0,0 +1,76 @@ +/* + * Copyright 2002-2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.http.converter.yaml; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.util.Assert; + +/** + * Implementation of {@link org.springframework.http.converter.HttpMessageConverter + * HttpMessageConverter} that can read and write the YAML + * data format using + * the dedicated Jackson 2.x extension. + * + *

By default, this converter supports the {@link MediaType#APPLICATION_YAML_VALE} + * media type. This can be overridden by setting the {@link #setSupportedMediaTypes + * supportedMediaTypes} property. + * + *

The default constructor uses the default configuration provided by + * {@link Jackson2ObjectMapperBuilder}. + * + * @author Hyoungjune Kim + * @since 6.2 + */ +public class MappingJackson2YamlHttpMessageConverter extends AbstractJackson2HttpMessageConverter { + + /** + * Construct a new {@code MappingJackson2YamlHttpMessageConverter} using the + * default configuration provided by {@code Jackson2ObjectMapperBuilder}. + */ + public MappingJackson2YamlHttpMessageConverter() { + this(Jackson2ObjectMapperBuilder.yaml().build()); + } + + /** + * Construct a new {@code MappingJackson2YamlHttpMessageConverter} with a + * custom {@link ObjectMapper} (must be configured with a {@code YAMLFactory} + * instance). + *

You can use {@link Jackson2ObjectMapperBuilder} to build it easily. + * @see Jackson2ObjectMapperBuilder#yaml() + */ + public MappingJackson2YamlHttpMessageConverter(ObjectMapper objectMapper) { + super(objectMapper, MediaType.APPLICATION_YAML); + Assert.isInstanceOf(YAMLFactory.class, objectMapper.getFactory(), "YAMLFactory required"); + } + + + /** + * {@inheritDoc} + * The {@code ObjectMapper} must be configured with a {@code YAMLFactory} instance. + */ + @Override + public void setObjectMapper(ObjectMapper objectMapper) { + Assert.isInstanceOf(YAMLFactory.class, objectMapper.getFactory(), "YAMLFactory required"); + super.setObjectMapper(objectMapper); + } + +} diff --git a/spring-web/src/main/java/org/springframework/http/converter/yaml/package-info.java b/spring-web/src/main/java/org/springframework/http/converter/yaml/package-info.java new file mode 100644 index 0000000000..18c07e5214 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/converter/yaml/package-info.java @@ -0,0 +1,9 @@ +/** + * Provides an HttpMessageConverter for the Yaml data format. + */ +@NonNullApi +@NonNullFields +package org.springframework.http.converter.yaml; + +import org.springframework.lang.NonNullApi; +import org.springframework.lang.NonNullFields; diff --git a/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java b/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java index 82e2a9f219..e2ae3b6867 100644 --- a/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java @@ -48,6 +48,7 @@ import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessag import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter; import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; +import org.springframework.http.converter.yaml.MappingJackson2YamlHttpMessageConverter; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -60,6 +61,7 @@ import org.springframework.web.util.UriTemplateHandler; * Default implementation of {@link RestClient.Builder}. * * @author Arjen Poutsma + * @author Hyoungjune Kim * @since 6.1 */ final class DefaultRestClientBuilder implements RestClient.Builder { @@ -86,6 +88,8 @@ final class DefaultRestClientBuilder implements RestClient.Builder { private static final boolean jackson2CborPresent; + private static final boolean jackson2YamlPresent; + static { ClassLoader loader = DefaultRestClientBuilder.class.getClassLoader(); @@ -101,6 +105,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder { kotlinSerializationJsonPresent = ClassUtils.isPresent("kotlinx.serialization.json.Json", loader); jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", loader); jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", loader); + jackson2YamlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.yaml.YAMLFactory", loader); } @Nullable @@ -394,6 +399,9 @@ final class DefaultRestClientBuilder implements RestClient.Builder { if (jackson2CborPresent) { this.messageConverters.add(new MappingJackson2CborHttpMessageConverter()); } + if (jackson2YamlPresent) { + this.messageConverters.add(new MappingJackson2YamlHttpMessageConverter()); + } } return this.messageConverters; } diff --git a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java index 78cace8148..343b42c5f2 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java @@ -66,6 +66,7 @@ import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageC import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; +import org.springframework.http.converter.yaml.MappingJackson2YamlHttpMessageConverter; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -108,6 +109,7 @@ import org.springframework.web.util.UriTemplateHandler; * @author Juergen Hoeller * @author Sam Brannen * @author Sebastien Deleuze + * @author Hyoungjune Kim * @since 3.0 * @see HttpMessageConverter * @see RequestCallback @@ -128,6 +130,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat private static final boolean jackson2CborPresent; + private static final boolean jackson2YamlPresent; + private static final boolean gsonPresent; private static final boolean jsonbPresent; @@ -149,6 +153,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader); jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader); jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader); + jackson2YamlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.yaml.YAMLFactory", classLoader); gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader); jsonbPresent = ClassUtils.isPresent("jakarta.json.bind.Jsonb", classLoader); kotlinSerializationCborPresent = ClassUtils.isPresent("kotlinx.serialization.cbor.Cbor", classLoader); @@ -222,6 +227,10 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat this.messageConverters.add(new KotlinSerializationCborHttpMessageConverter()); } + if (jackson2YamlPresent) { + this.messageConverters.add(new MappingJackson2YamlHttpMessageConverter()); + } + updateErrorHandlerConverters(); this.uriTemplateHandler = initUriTemplateHandler(); } diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilderTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilderTests.java index a6fc0b36f6..b8c927bac5 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilderTests.java @@ -79,6 +79,7 @@ import com.fasterxml.jackson.dataformat.cbor.CBORFactory; import com.fasterxml.jackson.dataformat.smile.SmileFactory; import com.fasterxml.jackson.dataformat.xml.XmlFactory; import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import kotlin.ranges.IntRange; import org.junit.jupiter.api.Test; @@ -95,6 +96,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * * @author Sebastien Deleuze * @author Eddú Meléndez + * @author Hyoungjune Kim */ @SuppressWarnings("deprecation") class Jackson2ObjectMapperBuilderTests { @@ -588,6 +590,13 @@ class Jackson2ObjectMapperBuilderTests { assertThat(objectMapper.getFactory().getClass()).isEqualTo(SmileFactory.class); } + @Test + void yaml() { + ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.yaml().build(); + assertThat(objectMapper).isNotNull(); + assertThat(objectMapper.getFactory().getClass()).isEqualTo(YAMLFactory.class); + } + @Test void visibility() throws JsonProcessingException { ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json() diff --git a/spring-webmvc/spring-webmvc.gradle b/spring-webmvc/spring-webmvc.gradle index 30e7567821..44661dd02c 100644 --- a/spring-webmvc/spring-webmvc.gradle +++ b/spring-webmvc/spring-webmvc.gradle @@ -19,6 +19,7 @@ dependencies { optional("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor") optional("com.fasterxml.jackson.dataformat:jackson-dataformat-smile") optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") + optional("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml") optional("com.github.librepdf:openpdf") optional("com.rometools:rome") optional("io.micrometer:context-propagation") diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java index b48e06b262..37daf14a1c 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -21,6 +21,7 @@ import java.util.Properties; import com.fasterxml.jackson.dataformat.cbor.CBORFactory; import com.fasterxml.jackson.dataformat.smile.SmileFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.w3c.dom.Element; import org.springframework.beans.factory.FactoryBean; @@ -55,6 +56,7 @@ import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageC import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; +import org.springframework.http.converter.yaml.MappingJackson2YamlHttpMessageConverter; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -148,6 +150,7 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv * @author Rossen Stoyanchev * @author Brian Clozel * @author Agim Emruli + * @author Hyoungjune Kim * @since 3.0 */ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { @@ -173,6 +176,8 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { private static final boolean jackson2CborPresent; + private static final boolean jackson2YamlPresent; + private static final boolean gsonPresent; static { @@ -185,6 +190,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader); jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader); jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader); + jackson2YamlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.yaml.YAMLFactory", classLoader); gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader); } @@ -463,6 +469,9 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { if (jackson2CborPresent) { defaultMediaTypes.put("cbor", MediaType.APPLICATION_CBOR_VALUE); } + if (jackson2YamlPresent) { + defaultMediaTypes.put("yaml", MediaType.APPLICATION_YAML_VALE); + } return defaultMediaTypes; } @@ -614,6 +623,14 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef); messageConverters.add(jacksonConverterDef); } + if(jackson2YamlPresent) { + Class type = MappingJackson2YamlHttpMessageConverter.class; + RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source); + GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source); + jacksonFactoryDef.getPropertyValues().add("factory", new RootBeanDefinition(YAMLFactory.class)); + jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef); + messageConverters.add(jacksonConverterDef); + } } return messageConverters; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index f78779465a..1324cafe54 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -58,6 +58,7 @@ import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageC import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; +import org.springframework.http.converter.yaml.MappingJackson2YamlHttpMessageConverter; import org.springframework.lang.Nullable; import org.springframework.util.AntPathMatcher; import org.springframework.util.Assert; @@ -183,6 +184,7 @@ import org.springframework.web.util.pattern.PathPatternParser; * @author Rossen Stoyanchev * @author Brian Clozel * @author Sebastien Deleuze + * @author Hyoungjune Kim * @since 3.1 * @see EnableWebMvc * @see WebMvcConfigurer @@ -201,6 +203,8 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv private static final boolean jackson2CborPresent; + private static final boolean jackson2YamlPresent; + private static final boolean gsonPresent; private static final boolean jsonbPresent; @@ -220,6 +224,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader); jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader); jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader); + jackson2YamlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.yaml.YAMLFactory", classLoader); gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader); jsonbPresent = ClassUtils.isPresent("jakarta.json.bind.Jsonb", classLoader); kotlinSerializationCborPresent = ClassUtils.isPresent("kotlinx.serialization.cbor.Cbor", classLoader); @@ -467,6 +472,9 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv if (jackson2CborPresent || kotlinSerializationCborPresent) { map.put("cbor", MediaType.APPLICATION_CBOR); } + if (jackson2YamlPresent) { + map.put("yaml", MediaType.APPLICATION_YAML); + } return map; } @@ -940,6 +948,13 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv } messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build())); } + if (jackson2YamlPresent) { + Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.yaml(); + if (this.applicationContext != null) { + builder.applicationContext(this.applicationContext); + } + messageConverters.add(new MappingJackson2YamlHttpMessageConverter(builder.build())); + } } /**