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 33f68d7fb9..282e386f6d 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 @@ -47,6 +47,8 @@ import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule; +import com.fasterxml.jackson.dataformat.xml.XmlFactory; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import org.springframework.beans.BeanUtils; @@ -127,6 +129,8 @@ public class Jackson2ObjectMapperBuilder { private ApplicationContext applicationContext; + private Boolean defaultUseWrapper; + /** * If set to {@code true}, an {@link XmlMapper} will be created using its @@ -392,6 +396,16 @@ public class Jackson2ObjectMapperBuilder { return this; } + /** + * Define if a wrapper will be used for indexed (List, array) properties or not by + * default (only applies to {@link XmlMapper}). + * @since 4.3 + */ + public Jackson2ObjectMapperBuilder defaultUseWrapper(boolean defaultUseWrapper) { + this.defaultUseWrapper = defaultUseWrapper; + return this; + } + /** * Specify features to enable. * @see com.fasterxml.jackson.core.JsonParser.Feature @@ -547,7 +561,8 @@ public class Jackson2ObjectMapperBuilder { public T build() { ObjectMapper mapper; if (this.createXmlMapper) { - mapper = new XmlObjectMapperInitializer().create(); + mapper = (this.defaultUseWrapper == null ? new XmlObjectMapperInitializer().create() + : new XmlObjectMapperInitializer().create(this.defaultUseWrapper)); } else { mapper = new ObjectMapper(); @@ -757,11 +772,21 @@ public class Jackson2ObjectMapperBuilder { private static class XmlObjectMapperInitializer { public ObjectMapper create() { + return new XmlMapper(xmlInputFactory()); + } + + public ObjectMapper create(boolean defaultUseWrapper) { + JacksonXmlModule module = new JacksonXmlModule(); + module.setDefaultUseWrapper(defaultUseWrapper); + return new XmlMapper(new XmlFactory(xmlInputFactory()), module); + } + + private static final XMLInputFactory xmlInputFactory() { XMLInputFactory inputFactory = XMLInputFactory.newInstance(); inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); inputFactory.setXMLResolver(NO_OP_XML_RESOLVER); - return new XmlMapper(inputFactory); + return inputFactory; } private static final XMLResolver NO_OP_XML_RESOLVER = new XMLResolver() { diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.java b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.java index fa35579364..0ab52307e0 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.java @@ -324,6 +324,15 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean container = new ListContainer<>(Arrays.asList("foo", "bar")); + String output = objectMapper.writeValueAsString(container); + assertThat(output, containsString("foobar")); + } + public static class CustomIntegerModule extends Module { @@ -501,4 +513,24 @@ public class Jackson2ObjectMapperBuilderTests { } } + public static class ListContainer { + + private List list; + + public ListContainer() { + } + + public ListContainer(List list) { + this.list = list; + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + } + }