mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
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:
+268
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
+11
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -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\""));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+12
-1
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user