Create a builder for Jackson ObjectMapper

Jackson2ObjectMapperBuilder now allows to create ObjectMapper and XmlMapper
instances easily thanks to its fluent API.

This builder is used in Jackson message converters and views to instantiate default
ObjectMapper and XmlMapper.

This commit also add a createXmlMapper property to
Jackson2ObjectMapperFactoryBean in order to allow to create easily a XmlMapper
instance.

Issue: SPR-12243
This commit is contained in:
Sebastien Deleuze
2014-09-13 21:47:19 -05:00
parent 521bbfcf56
commit 4e24d66ff7
16 changed files with 935 additions and 26 deletions

View File

@@ -0,0 +1,268 @@
/*
* Copyright 2002-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.
* You may obtain a copy of the License at
*
* http://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.json;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
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.databind.cfg.DeserializerFactoryConfig;
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
import com.fasterxml.jackson.databind.deser.BasicDeserializerFactory;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BasicSerializerFactory;
import com.fasterxml.jackson.databind.ser.Serializers;
import com.fasterxml.jackson.databind.ser.std.ClassSerializer;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.junit.Test;
import org.springframework.beans.FatalBeanException;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
/**
* Test class for {@link Jackson2ObjectMapperBuilder}.
*
* @author Sebastien Deleuze
*/
public class Jackson2ObjectMapperBuilderTests {
private static final String DATE_FORMAT = "yyyy-MM-dd";
@Test
public void settersWithNullValues() {
// Should not crash:
Jackson2ObjectMapperBuilder.json().serializers((JsonSerializer<?>[]) null)
.serializersByType(null).deserializersByType(null)
.featuresToEnable((Object[]) null).featuresToDisable((Object[]) null);
}
@Test(expected = FatalBeanException.class)
public void unknownFeature() {
Jackson2ObjectMapperBuilder.json().featuresToEnable(Boolean.TRUE).build();
}
@Test
public void defaultProperties() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
assertNotNull(objectMapper);
assertTrue(objectMapper.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION));
assertTrue(objectMapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
assertTrue(objectMapper.isEnabled(MapperFeature.AUTO_DETECT_FIELDS));
assertTrue(objectMapper.isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
assertTrue(objectMapper.isEnabled(MapperFeature.AUTO_DETECT_SETTERS));
assertFalse(objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT));
assertTrue(objectMapper.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS));
}
@Test
public void propertiesShortcut() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().autoDetectFields(false)
.defaultViewInclusion(true).failOnUnknownProperties(true).failOnEmptyBeans(false)
.autoDetectGettersSetters(false).indentOutput(true).build();
assertNotNull(objectMapper);
assertTrue(objectMapper.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION));
assertTrue(objectMapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
assertFalse(objectMapper.isEnabled(MapperFeature.AUTO_DETECT_FIELDS));
assertFalse(objectMapper.isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
assertFalse(objectMapper.isEnabled(MapperFeature.AUTO_DETECT_SETTERS));
assertTrue(objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT));
assertFalse(objectMapper.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS));
}
@Test
public void booleanSetters() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
.featuresToEnable(MapperFeature.DEFAULT_VIEW_INCLUSION,
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
SerializationFeature.INDENT_OUTPUT)
.featuresToDisable(MapperFeature.AUTO_DETECT_FIELDS,
MapperFeature.AUTO_DETECT_GETTERS,
MapperFeature.AUTO_DETECT_SETTERS,
SerializationFeature.FAIL_ON_EMPTY_BEANS).build();
assertNotNull(objectMapper);
assertTrue(objectMapper.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION));
assertTrue(objectMapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
assertFalse(objectMapper.isEnabled(MapperFeature.AUTO_DETECT_FIELDS));
assertFalse(objectMapper.isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
assertFalse(objectMapper.isEnabled(MapperFeature.AUTO_DETECT_SETTERS));
assertTrue(objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT));
assertFalse(objectMapper.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS));
}
@Test
public void setNotNullSerializationInclusion() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
assertTrue(objectMapper.getSerializationConfig().getSerializationInclusion() ==
JsonInclude.Include.ALWAYS);
objectMapper = Jackson2ObjectMapperBuilder.json().serializationInclusion(JsonInclude.Include.NON_NULL).build();
assertTrue(objectMapper.getSerializationConfig().getSerializationInclusion() == JsonInclude.Include.NON_NULL);
}
@Test
public void setNotDefaultSerializationInclusion() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
assertTrue(objectMapper.getSerializationConfig().getSerializationInclusion() == JsonInclude.Include.ALWAYS);
objectMapper = Jackson2ObjectMapperBuilder.json().serializationInclusion(JsonInclude.Include.NON_DEFAULT).build();
assertTrue(objectMapper.getSerializationConfig().getSerializationInclusion() == JsonInclude.Include.NON_DEFAULT);
}
@Test
public void setNotEmptySerializationInclusion() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
assertTrue(objectMapper.getSerializationConfig().getSerializationInclusion() == JsonInclude.Include.ALWAYS);
objectMapper = Jackson2ObjectMapperBuilder.json().serializationInclusion(JsonInclude.Include.NON_EMPTY).build();
assertTrue(objectMapper.getSerializationConfig().getSerializationInclusion() == JsonInclude.Include.NON_EMPTY);
}
@Test
public void dateTimeFormatSetter() {
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().dateFormat(dateFormat).build();
assertEquals(dateFormat, objectMapper.getSerializationConfig().getDateFormat());
assertEquals(dateFormat, objectMapper.getDeserializationConfig().getDateFormat());
}
@Test
public void simpleDateFormatStringSetter() {
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().simpleDateFormat(DATE_FORMAT).build();
assertEquals(dateFormat, objectMapper.getSerializationConfig().getDateFormat());
assertEquals(dateFormat, objectMapper.getDeserializationConfig().getDateFormat());
}
@Test
public void setModules() {
NumberSerializer serializer1 = new NumberSerializer();
SimpleModule module = new SimpleModule();
module.addSerializer(Integer.class, serializer1);
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(Arrays.asList(new Module[]{module})).build();
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null) == serializer1);
}
private static SerializerFactoryConfig getSerializerFactoryConfig(ObjectMapper objectMapper) {
return ((BasicSerializerFactory) objectMapper.getSerializerFactory()).getFactoryConfig();
}
private static DeserializerFactoryConfig getDeserializerFactoryConfig(ObjectMapper objectMapper) {
return ((BasicDeserializerFactory) objectMapper.getDeserializationContext().getFactory()).getFactoryConfig();
}
@Test
public void propertyNamingStrategy() {
PropertyNamingStrategy strategy = new PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy();
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().propertyNamingStrategy(strategy).build();
assertSame(strategy, objectMapper.getSerializationConfig().getPropertyNamingStrategy());
assertSame(strategy, objectMapper.getDeserializationConfig().getPropertyNamingStrategy());
}
@Test
public void completeSetup() {
NopAnnotationIntrospector annotationIntrospector = NopAnnotationIntrospector.instance;
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.instance(new ObjectMapper());
Map<Class<?>, JsonDeserializer<?>>
deserializers = new HashMap<Class<?>, JsonDeserializer<?>>();
deserializers.put(Date.class, new DateDeserializers.DateDeserializer());
JsonSerializer<Class<?>> serializer1 = new ClassSerializer();
JsonSerializer<Number> serializer2 = new NumberSerializer();
builder.serializers(serializer1);
builder.serializersByType(Collections
.<Class<?>, JsonSerializer<?>>singletonMap(Boolean.class, serializer2));
builder.deserializersByType(deserializers);
builder.annotationIntrospector(annotationIntrospector);
builder.featuresToEnable(SerializationFeature.FAIL_ON_EMPTY_BEANS,
DeserializationFeature.UNWRAP_ROOT_VALUE,
JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER,
JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
builder.featuresToDisable(MapperFeature.AUTO_DETECT_GETTERS,
MapperFeature.AUTO_DETECT_FIELDS, JsonParser.Feature.AUTO_CLOSE_SOURCE,
JsonGenerator.Feature.QUOTE_FIELD_NAMES);
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
ObjectMapper objectMapper = builder.build();
assertTrue(getSerializerFactoryConfig(objectMapper).hasSerializers());
assertTrue(getDeserializerFactoryConfig(objectMapper).hasDeserializers());
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
assertTrue(serializers.findSerializer(null, SimpleType.construct(Class.class), null) == serializer1);
assertTrue(serializers.findSerializer(null, SimpleType.construct(Boolean.class), null) == serializer2);
assertNull(serializers.findSerializer(null, SimpleType.construct(Number.class), null));
assertTrue(annotationIntrospector == objectMapper.getSerializationConfig().getAnnotationIntrospector());
assertTrue(annotationIntrospector == objectMapper.getDeserializationConfig().getAnnotationIntrospector());
assertTrue(objectMapper.getSerializationConfig().isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS));
assertTrue(objectMapper.getDeserializationConfig().isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
assertTrue(objectMapper.getFactory().isEnabled(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER));
assertTrue(objectMapper.getFactory().isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS));
assertFalse(objectMapper.getSerializationConfig().isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
assertTrue(objectMapper.getDeserializationConfig().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION));
assertTrue(objectMapper.getDeserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
assertFalse(objectMapper.getDeserializationConfig().isEnabled(MapperFeature.AUTO_DETECT_FIELDS));
assertFalse(objectMapper.getFactory().isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
assertFalse(objectMapper.getFactory().isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES));
assertTrue(objectMapper.getSerializationConfig().getSerializationInclusion() == JsonInclude.Include.NON_NULL);
}
@Test
public void xmlMapper() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.xml().build();
assertNotNull(objectMapper);
assertEquals(XmlMapper.class, objectMapper.getClass());
}
@Test
public void createXmlMapper() {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json().indentOutput(true);
ObjectMapper jsonObjectMapper = builder.build();
ObjectMapper xmlObjectMapper = builder.createXmlMapper(true).build();
assertTrue(jsonObjectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT));
assertTrue(xmlObjectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT));
assertTrue(xmlObjectMapper.getClass().isAssignableFrom(XmlMapper.class));
}
}

View File

@@ -260,6 +260,7 @@ public class Jackson2ObjectMapperFactoryBeanTests {
assertFalse(objectMapper.getSerializationConfig().isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
assertTrue(objectMapper.getDeserializationConfig().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION));
assertTrue(objectMapper.getDeserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
assertFalse(objectMapper.getDeserializationConfig().isEnabled(MapperFeature.AUTO_DETECT_FIELDS));
assertFalse(objectMapper.getFactory().isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
assertFalse(objectMapper.getFactory().isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES));
@@ -276,4 +277,14 @@ public class Jackson2ObjectMapperFactoryBeanTests {
assertEquals(XmlMapper.class, this.factory.getObjectType());
}
@Test
public void createXmlMapper() {
this.factory.setCreateXmlMapper(true);
this.factory.afterPropertiesSet();
assertNotNull(this.factory.getObject());
assertTrue(this.factory.isSingleton());
assertEquals(XmlMapper.class, this.factory.getObjectType());
}
}

View File

@@ -253,8 +253,8 @@ public class MappingJackson2HttpMessageConverterTests {
String result = outputMessage.getBodyAsString(Charset.forName("UTF-8"));
assertThat(result, containsString("\"withView1\":\"with\""));
assertThat(result, containsString("\"withoutView\":\"without\""));
assertThat(result, not(containsString("\"withView2\":\"with\"")));
assertThat(result, containsString("\"withoutView\":\"without\""));
}
@Test
@@ -286,8 +286,8 @@ public class MappingJackson2HttpMessageConverterTests {
assertThat(result, startsWith("callback("));
assertThat(result, endsWith(");"));
assertThat(result, containsString("\"withView1\":\"with\""));
assertThat(result, containsString("\"withoutView\":\"without\""));
assertThat(result, not(containsString("\"withView2\":\"with\"")));
assertThat(result, containsString("\"withoutView\":\"without\""));
}

View File

@@ -22,6 +22,7 @@ import java.lang.reflect.Method;
import java.nio.charset.Charset;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
@@ -123,8 +124,14 @@ public class MappingJackson2XmlHttpMessageConverterTests {
String result = outputMessage.getBodyAsString(Charset.forName("UTF-8"));
assertThat(result, containsString("<withView1>with</withView1>"));
assertThat(result, containsString("<withoutView>without</withoutView>"));
assertThat(result, not(containsString("<withView2>with</withView2>")));
assertThat(result, containsString("<withoutView>without</withoutView>"));
}
@Test
public void customXmlMapper() {
new MappingJackson2XmlHttpMessageConverter(new MyXmlMapper());
// Assert no exception is thrown
}
private void writeInternal(Object object, HttpOutputMessage outputMessage)
@@ -238,4 +245,8 @@ public class MappingJackson2XmlHttpMessageConverterTests {
}
}
private static class MyXmlMapper extends XmlMapper {
}
}