diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml
index d880067a3d3..07634036b62 100644
--- a/spring-boot-autoconfigure/pom.xml
+++ b/spring-boot-autoconfigure/pom.xml
@@ -40,6 +40,11 @@
jackson-databind
true
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ true
+
com.fasterxml.jackson.datatype
jackson-datatype-joda
@@ -104,6 +109,12 @@
org.apache.solr
solr-solrj
true
+
+
+ org.codehaus.woodstox
+ wstx-asl
+
+
org.apache.tomcat.embed
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java
index bc86a82c676..6301babf85d 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java
@@ -20,6 +20,7 @@ import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
+import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.PostConstruct;
@@ -29,41 +30,33 @@ import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnJava;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.JavaVersion;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.web.HttpMapperProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.datatype.joda.JodaModule;
-import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
/**
* Auto configuration for Jackson. The following auto-configuration will get applied:
*
* - an {@link ObjectMapper} in case none is already configured.
- * - the {@link JodaModule} registered if it's on the classpath.
- * - the {@link JSR310Module} registered if it's on the classpath and the application is
- * running on Java 8 or better.
+ * - a {@link Jackson2ObjectMapperBuilder} in case none is already configured.
* - auto-registration for all {@link Module} beans with all {@link ObjectMapper} beans
* (including the defaulted ones).
*
*
* @author Oliver Gierke
* @author Andy Wilkinson
+ * @author Sebastien Deleuze
* @author Marcel Overdijk
* @since 1.1.0
*/
@@ -88,10 +81,23 @@ public class JacksonAutoConfiguration {
}
@Configuration
- @ConditionalOnClass(ObjectMapper.class)
- @EnableConfigurationProperties({ HttpMapperProperties.class, JacksonProperties.class })
+ @ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class })
static class JacksonObjectMapperAutoConfiguration {
+ @Bean
+ @Primary
+ @ConditionalOnMissingBean(ObjectMapper.class)
+ public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
+ return builder.createXmlMapper(false).build();
+ }
+
+ }
+
+ @Configuration
+ @ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class })
+ @EnableConfigurationProperties({ HttpMapperProperties.class, JacksonProperties.class })
+ static class JacksonObjectMapperBuilderAutoConfiguration {
+
@Autowired
private HttpMapperProperties httpMapperProperties = new HttpMapperProperties();
@@ -99,29 +105,39 @@ public class JacksonAutoConfiguration {
private JacksonProperties jacksonProperties = new JacksonProperties();
@Bean
- @Primary
- @ConditionalOnMissingBean
- public ObjectMapper jacksonObjectMapper() {
- ObjectMapper objectMapper = new ObjectMapper();
+ @ConditionalOnMissingBean(Jackson2ObjectMapperBuilder.class)
+ public Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder() {
+ Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
if (this.httpMapperProperties.isJsonSortKeys()) {
- objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,
- true);
+ builder.featuresToEnable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);
}
- configureDeserializationFeatures(objectMapper);
- configureSerializationFeatures(objectMapper);
- configureMapperFeatures(objectMapper);
- configureParserFeatures(objectMapper);
- configureGeneratorFeatures(objectMapper);
+ configureFeatures(builder, this.jacksonProperties.getDeserialization());
+ configureFeatures(builder, this.jacksonProperties.getSerialization());
+ configureFeatures(builder, this.jacksonProperties.getMapper());
+ configureFeatures(builder, this.jacksonProperties.getParser());
+ configureFeatures(builder, this.jacksonProperties.getGenerator());
+
+ configureDateFormat(builder);
+ configurePropertyNamingStrategy(builder);
- configureDateFormat(objectMapper);
- configurePropertyNamingStrategy(objectMapper);
+ return builder;
+ }
- return objectMapper;
+ private void configureFeatures(Jackson2ObjectMapperBuilder builder,
+ Map, Boolean> features) {
+ for (Entry, Boolean> entry : features.entrySet()) {
+ if (entry.getValue() != null && entry.getValue()) {
+ builder.featuresToEnable(entry.getKey());
+ }
+ else {
+ builder.featuresToDisable(entry.getKey());
+ }
+ }
}
- private void configurePropertyNamingStrategy(ObjectMapper objectMapper) {
+ private void configurePropertyNamingStrategy(Jackson2ObjectMapperBuilder builder) {
// We support a fully qualified class name extending Jackson's
// PropertyNamingStrategy or a string value corresponding to the constant
// names in PropertyNamingStrategy which hold default provided implementations
@@ -130,9 +146,8 @@ public class JacksonAutoConfiguration {
if (propertyNamingStrategy != null) {
try {
Class> clazz = ClassUtils.forName(propertyNamingStrategy, null);
- objectMapper
- .setPropertyNamingStrategy((PropertyNamingStrategy) BeanUtils
- .instantiateClass(clazz));
+ builder.propertyNamingStrategy((PropertyNamingStrategy) BeanUtils
+ .instantiateClass(clazz));
}
catch (ClassNotFoundException e) {
// Find the field (this way we automatically support new constants
@@ -141,9 +156,8 @@ public class JacksonAutoConfiguration {
propertyNamingStrategy, PropertyNamingStrategy.class);
if (field != null) {
try {
- objectMapper
- .setPropertyNamingStrategy((PropertyNamingStrategy) field
- .get(null));
+ builder.propertyNamingStrategy((PropertyNamingStrategy) field
+ .get(null));
}
catch (Exception ex) {
throw new IllegalStateException(ex);
@@ -158,85 +172,21 @@ public class JacksonAutoConfiguration {
}
}
- private void configureDateFormat(ObjectMapper objectMapper) {
+ private void configureDateFormat(Jackson2ObjectMapperBuilder builder) {
// We support a fully qualified class name extending DateFormat or a date
// pattern string value
String dateFormat = this.jacksonProperties.getDateFormat();
if (dateFormat != null) {
try {
Class> clazz = ClassUtils.forName(dateFormat, null);
- objectMapper.setDateFormat((DateFormat) BeanUtils
- .instantiateClass(clazz));
+ builder.dateFormat((DateFormat) BeanUtils.instantiateClass(clazz));
}
catch (ClassNotFoundException e) {
- objectMapper.setDateFormat(new SimpleDateFormat(dateFormat));
+ builder.dateFormat(new SimpleDateFormat(dateFormat));
}
}
}
- private void configureDeserializationFeatures(ObjectMapper objectMapper) {
- for (Entry entry : this.jacksonProperties
- .getDeserialization().entrySet()) {
- objectMapper.configure(entry.getKey(), isFeatureEnabled(entry));
- }
- }
-
- private void configureSerializationFeatures(ObjectMapper objectMapper) {
- for (Entry entry : this.jacksonProperties
- .getSerialization().entrySet()) {
- objectMapper.configure(entry.getKey(), isFeatureEnabled(entry));
- }
- }
-
- private void configureMapperFeatures(ObjectMapper objectMapper) {
- for (Entry entry : this.jacksonProperties.getMapper()
- .entrySet()) {
- objectMapper.configure(entry.getKey(), isFeatureEnabled(entry));
- }
- }
-
- private void configureParserFeatures(ObjectMapper objectMapper) {
- for (Entry entry : this.jacksonProperties
- .getParser().entrySet()) {
- objectMapper.configure(entry.getKey(), isFeatureEnabled(entry));
- }
- }
-
- private void configureGeneratorFeatures(ObjectMapper objectMapper) {
- for (Entry entry : this.jacksonProperties
- .getGenerator().entrySet()) {
- objectMapper.configure(entry.getKey(), isFeatureEnabled(entry));
- }
- }
-
- private boolean isFeatureEnabled(Entry, Boolean> entry) {
- return entry.getValue() != null && entry.getValue();
- }
- }
-
- @Configuration
- @ConditionalOnClass(JodaModule.class)
- static class JodaModuleAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- public JodaModule jacksonJodaModule() {
- return new JodaModule();
- }
-
- }
-
- @Configuration
- @ConditionalOnJava(JavaVersion.EIGHT)
- @ConditionalOnClass(JSR310Module.class)
- static class Jsr310ModuleAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- public JSR310Module jacksonJsr310Module() {
- return new JSR310Module();
- }
-
}
}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConverters.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConverters.java
index 4b0290e1b1d..db752ddd0db 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConverters.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConverters.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2013 the original author or authors.
+ * Copyright 2012-2014 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.
@@ -25,6 +25,7 @@ import java.util.List;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.xml.AbstractXmlHttpMessageConverter;
+import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.util.ClassUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@@ -141,7 +142,8 @@ public class HttpMessageConverters implements Iterable>
for (Iterator> iterator = converters.iterator(); iterator
.hasNext();) {
HttpMessageConverter> converter = iterator.next();
- if (converter instanceof AbstractXmlHttpMessageConverter) {
+ if ((converter instanceof AbstractXmlHttpMessageConverter)
+ || (converter instanceof MappingJackson2XmlHttpMessageConverter)) {
xml.add(converter);
iterator.remove();
}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfiguration.java
index 68b5b910d7c..23827a2a4fb 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfiguration.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfiguration.java
@@ -29,9 +29,12 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.google.gson.Gson;
/**
@@ -42,6 +45,7 @@ import com.google.gson.Gson;
* @author Piotr Maj
* @author Oliver Gierke
* @author David Liu
+ * @author Sebastien Deleuze
* @author Andy Wilkinson
*/
@Configuration
@@ -78,6 +82,27 @@ public class HttpMessageConvertersAutoConfiguration {
}
+ @Configuration
+ @ConditionalOnClass(XmlMapper.class)
+ @ConditionalOnBean(Jackson2ObjectMapperBuilder.class)
+ @EnableConfigurationProperties(HttpMapperProperties.class)
+ protected static class XmlMappers {
+
+ @Autowired
+ private HttpMapperProperties properties = new HttpMapperProperties();
+
+ @Bean
+ @ConditionalOnMissingBean
+ public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter(
+ Jackson2ObjectMapperBuilder builder) {
+ MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter();
+ converter.setObjectMapper(builder.createXmlMapper(true).build());
+ converter.setPrettyPrint(this.properties.isJsonPrettyPrint());
+ return converter;
+ }
+
+ }
+
@Configuration
@ConditionalOnClass(Gson.class)
@ConditionalOnBean(Gson.class)
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java
index 4b5ba52888f..af34adacaf2 100644
--- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java
@@ -19,7 +19,6 @@ package org.springframework.boot.autoconfigure.jackson;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
-import java.util.Map;
import org.joda.time.DateTime;
import org.joda.time.LocalDateTime;
@@ -33,6 +32,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
@@ -45,11 +45,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
-import com.fasterxml.jackson.datatype.joda.JodaModule;
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.hasItem;
-import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -63,6 +60,7 @@ import static org.mockito.Mockito.verify;
*
* @author Dave Syer
* @author Oliver Gierke
+ * @author Sebastien Deleuze
* @author Andy Wilkinson
* @author Marcel Overdijk
*/
@@ -86,9 +84,6 @@ public class JacksonAutoConfigurationTests {
public void registersJodaModuleAutomatically() {
this.context.register(JacksonAutoConfiguration.class);
this.context.refresh();
- Map modules = this.context.getBeansOfType(Module.class);
- assertThat(modules.size(), greaterThanOrEqualTo(1)); // Depends on the JDK
- assertThat(modules.get("jacksonJodaModule"), is(instanceOf(JodaModule.class)));
ObjectMapper objectMapper = this.context.getBean(ObjectMapper.class);
assertThat(objectMapper.canSerialize(LocalDateTime.class), is(true));
}
@@ -339,6 +334,26 @@ public class JacksonAutoConfigurationTests {
.isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET));
}
+ @Test
+ public void defaultObjectMapperBuilder() throws Exception {
+ this.context.register(JacksonAutoConfiguration.class);
+ this.context.refresh();
+ Jackson2ObjectMapperBuilder builder = this.context
+ .getBean(Jackson2ObjectMapperBuilder.class);
+ ObjectMapper mapper = builder.build();
+ assertTrue(MapperFeature.DEFAULT_VIEW_INCLUSION.enabledByDefault());
+ assertFalse(mapper.getDeserializationConfig().isEnabled(
+ MapperFeature.DEFAULT_VIEW_INCLUSION));
+ assertTrue(MapperFeature.DEFAULT_VIEW_INCLUSION.enabledByDefault());
+ assertFalse(mapper.getDeserializationConfig().isEnabled(
+ MapperFeature.DEFAULT_VIEW_INCLUSION));
+ assertFalse(mapper.getSerializationConfig().isEnabled(
+ MapperFeature.DEFAULT_VIEW_INCLUSION));
+ assertTrue(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES.enabledByDefault());
+ assertFalse(mapper.getDeserializationConfig().isEnabled(
+ DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
+ }
+
@Configuration
protected static class ModulesConfig {
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfigurationTests.java
index 1c4368101cc..99cddea47f2 100644
--- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfigurationTests.java
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfigurationTests.java
@@ -25,7 +25,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
@@ -40,6 +42,7 @@ import static org.junit.Assert.assertTrue;
* @author Oliver Gierke
* @author David Liu
* @author Andy Wilkinson
+ * @author Sebastien Deleuze
*/
public class HttpMessageConvertersAutoConfigurationTests {
@@ -59,11 +62,13 @@ public class HttpMessageConvertersAutoConfigurationTests {
assertTrue(this.context.getBeansOfType(ObjectMapper.class).isEmpty());
assertTrue(this.context.getBeansOfType(MappingJackson2HttpMessageConverter.class)
.isEmpty());
+ assertTrue(this.context.getBeansOfType(
+ MappingJackson2XmlHttpMessageConverter.class).isEmpty());
}
@Test
public void defaultJacksonConverter() throws Exception {
- this.context.register(JacksonConfig.class,
+ this.context.register(JacksonObjectMapperConfig.class,
HttpMessageConvertersAutoConfiguration.class);
this.context.refresh();
@@ -73,9 +78,25 @@ public class HttpMessageConvertersAutoConfigurationTests {
assertConverterBeanRegisteredWithHttpMessageConverters(MappingJackson2HttpMessageConverter.class);
}
+ @Test
+ public void defaultJacksonConvertersWithBuilder() throws Exception {
+ this.context.register(JacksonObjectMapperBuilderConfig.class,
+ HttpMessageConvertersAutoConfiguration.class);
+ this.context.refresh();
+
+ assertConverterBeanExists(MappingJackson2HttpMessageConverter.class,
+ "mappingJackson2HttpMessageConverter");
+ assertConverterBeanExists(MappingJackson2XmlHttpMessageConverter.class,
+ "mappingJackson2XmlHttpMessageConverter");
+
+ assertConverterBeanRegisteredWithHttpMessageConverters(MappingJackson2HttpMessageConverter.class);
+ assertConverterBeanRegisteredWithHttpMessageConverters(MappingJackson2XmlHttpMessageConverter.class);
+ }
+
@Test
public void customJacksonConverter() throws Exception {
- this.context.register(JacksonConfig.class, JacksonConverterConfig.class,
+ this.context.register(JacksonObjectMapperConfig.class,
+ JacksonConverterConfig.class,
HttpMessageConvertersAutoConfiguration.class);
this.context.refresh();
@@ -128,13 +149,27 @@ public class HttpMessageConvertersAutoConfigurationTests {
}
@Configuration
- protected static class JacksonConfig {
+ protected static class JacksonObjectMapperConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}
+ @Configuration
+ protected static class JacksonObjectMapperBuilderConfig {
+
+ @Bean
+ public ObjectMapper objectMapper() {
+ return new ObjectMapper();
+ }
+
+ @Bean
+ public Jackson2ObjectMapperBuilder builder() {
+ return new Jackson2ObjectMapperBuilder();
+ }
+ }
+
@Configuration
protected static class JacksonConverterConfig {
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersTests.java
index 6363679f1be..f5fa9b517c7 100644
--- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersTests.java
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2013 the original author or authors.
+ * Copyright 2012-2014 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.
@@ -30,7 +30,7 @@ import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
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.xml.SourceHttpMessageConverter;
import static org.hamcrest.Matchers.equalTo;
@@ -63,7 +63,7 @@ public class HttpMessageConvertersTests {
ResourceHttpMessageConverter.class, SourceHttpMessageConverter.class,
AllEncompassingFormHttpMessageConverter.class,
MappingJackson2HttpMessageConverter.class,
- Jaxb2RootElementHttpMessageConverter.class)));
+ MappingJackson2XmlHttpMessageConverter.class)));
}
@Test
@@ -106,7 +106,7 @@ public class HttpMessageConvertersTests {
List> converters) {
for (Iterator> iterator = converters.iterator(); iterator
.hasNext();) {
- if (iterator.next() instanceof Jaxb2RootElementHttpMessageConverter) {
+ if (iterator.next() instanceof MappingJackson2XmlHttpMessageConverter) {
iterator.remove();
}
}
diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml
index c976e95db3d..05b56297087 100644
--- a/spring-boot-dependencies/pom.xml
+++ b/spring-boot-dependencies/pom.xml
@@ -440,6 +440,11 @@
jackson-databind
${jackson.version}
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ ${jackson.version}
+
com.fasterxml.jackson.datatype
jackson-datatype-joda
diff --git a/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-docs/src/main/asciidoc/howto.adoc
index 890008f3116..0c4a58da468 100644
--- a/spring-boot-docs/src/main/asciidoc/howto.adoc
+++ b/spring-boot-docs/src/main/asciidoc/howto.adoc
@@ -643,15 +643,40 @@ default as long as Jackson2 is on the classpath. For example:
As long as `MyThing` can be serialized by Jackson2 (e.g. a normal POJO or Groovy object)
then `http://localhost:8080/thing` will serve a JSON representation of it by default.
-Sometimes in a browser you might see XML responses (but by default only if `MyThing` was
-a JAXB object) because browsers tend to send accept headers that prefer XML.
+Sometimes in a browser you might see XML responses because browsers tend to send accept
+headers that prefer XML.
[[howto-write-an-xml-rest-service]]
=== Write an XML REST service
-Since JAXB is in the JDK the same example as we used for JSON would work, as long as the
-`MyThing` was annotated as `@XmlRootElement`:
+If you have the Jackson XML extension (`jackson-dataformat-xml`) on the classpath, it will
+be used to render XML responses and the very same example as we used for JSON would work.
+To use it, add the following dependency to your project:
+
+[source,xml,indent=0,subs="verbatim,quotes,attributes"]
+----
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+
+----
+
+You may also want to add a dependency on Woodstox. It's faster than the default Stax
+implementation provided by the JDK and also adds pretty print support and improved
+namespace handling:
+
+[source,xml,indent=0,subs="verbatim,quotes,attributes"]
+----
+
+ org.codehaus.woodstox
+ woodstox-core-asl
+
+----
+
+If Jackson's XML extension is not available, JAXB (provided by default in the JDK) will
+be used, with the additional requirement to have `MyThing` annotated as
+`@XmlRootElement`:
[source,java,indent=0,subs="verbatim,quotes,attributes"]
----
@@ -670,14 +695,21 @@ To get the server to render XML instead of JSON you might have to send an
[[howto-customize-the-jackson-objectmapper]]
=== Customize the Jackson ObjectMapper
Spring MVC (client and server side) uses `HttpMessageConverters` to negotiate content
-conversion in an HTTP exchange. If Jackson is on the classpath you already get a default
-converter with a vanilla `ObjectMapper`. Spring Boot has some features to make it easier
-to customize this behavior.
+conversion in an HTTP exchange. If Jackson is on the classpath you already get the
+default converter(s) provided by `Jackson2ObjectMapperBuilder`.
-You can configure the vanilla `ObjectMapper` using the environment. Jackson provides an
-extensive suite of simple on/off features that can be used to configure various aspects
-of its processing. These features are described in five enums in Jackson which map onto
-properties in the environment:
+The `ObjectMapper` (or `XmlMapper` for Jackson XML converter) instance created by default
+have the following customized properties:
+
+* MapperFeature.DEFAULT_VIEW_INCLUSION is disabled
+* DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES is disabled
+
+Spring Boot has also some features to make it easier to customize this behavior.
+
+You can configure the `ObjectMapper` and `XmlMapper` instances using the environment.
+Jackson provides an extensive suite of simple on/off features that can be used to
+configure various aspects of its processing. These features are described in five enums in
+Jackson which map onto properties in the environment:
|===
|Jackson enum|Environment property
@@ -698,14 +730,17 @@ properties in the environment:
|`spring.jackson.serialization.=true\|false`
|===
-For example, to allow deserialization to continue when an unknown property is encountered
-during deserialization, set `spring.jackson.deserialization.fail_on_unknown_properties=false`.
-Note that, thanks to the use of <>,
-the case of `fail_on_unknown_properties` doesn't have to match the case of the corresponding
-enum constant which is `FAIL_ON_UNKNOWN_PROPERTIES`.
+For example, to enable pretty print, set `spring.jackson.serialization.indent_output=true`.
+Note that, thanks to the use of <>, the case of `indent_output` doesn't have to match the case of the
+corresponding enum constant which is `INDENT_OUTPUT`.
+
+If you want to replace the default `ObjectMapper` completely, define a `@Bean` of that
+type and mark it as `@Primary`.
-If you want to replace the default `ObjectMapper` completely, define a `@Bean` of that type
-and mark it as `@Primary`.
+Defining a `@Bean` of type `Jackson2ObjectMapperBuilder` will allow you to customize both
+default `ObjectMapper` and `XmlMapper` (used in `MappingJackson2HttpMessageConverter` and
+`MappingJackson2XmlHttpMessageConverter` respectively).
Another way to customize Jackson is to add beans of type
`com.fasterxml.jackson.databind.Module` to your context. They will be registered with every
diff --git a/spring-boot-docs/src/main/asciidoc/index.adoc b/spring-boot-docs/src/main/asciidoc/index.adoc
index e3386f7dad0..8055def48ed 100644
--- a/spring-boot-docs/src/main/asciidoc/index.adoc
+++ b/spring-boot-docs/src/main/asciidoc/index.adoc
@@ -1,5 +1,5 @@
= Spring Boot Reference Guide
-Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson; Marcel Overdijk; Christian Dupuis;
+Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson; Marcel Overdijk; Christian Dupuis; Sébastien Deleuze
:doctype: book
:toc:
:toclevels: 4
diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
index a6de6df80aa..21984975e06 100644
--- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
+++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
@@ -885,7 +885,8 @@ formatters, view controllers etc.) you can add your own `@Bean` of type
==== HttpMessageConverters
Spring MVC uses the `HttpMessageConverter` interface to convert HTTP requests and
responses. Sensible defaults are included out of the box, for example Objects can be
-automatically converted to JSON (using the Jackson library) or XML (using JAXB).
+automatically converted to JSON (using the Jackson library) or XML (using the Jackson
+XML extension if available, else using JAXB).
If you need to add or customize converters you can use Spring Boot's
`HttpMessageConverters` class: