Browse Source

Jackson2ObjectMapperFactoryBean builds on revised Jackson2ObjectMapperBuilder now

Issue: SPR-12243
pull/655/merge
Juergen Hoeller 11 years ago
parent
commit
d778037f40
  1. 187
      spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java
  2. 272
      spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.java
  3. 14
      spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilderTests.java
  4. 2
      spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBeanTests.java

187
spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java

@ -47,10 +47,10 @@ import org.springframework.util.ClassUtils; @@ -47,10 +47,10 @@ import org.springframework.util.ClassUtils;
/**
* A builder used to create {@link ObjectMapper} instances with a fluent API.
*
* <p>It customizes Jackson defaults properties with the following ones:
* <p>It customizes Jackson's default properties with the following ones:
* <ul>
* <li>{@link MapperFeature#DEFAULT_VIEW_INCLUSION} is disabled</li>
* <li>{@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} is disabled</li>
* <li>{@link MapperFeature#DEFAULT_VIEW_INCLUSION} is disabled</li>
* <li>{@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} is disabled</li>
* </ul>
*
* <p>Note that Jackson's JSR-310 and Joda-Time support modules will be registered automatically
@ -59,28 +59,27 @@ import org.springframework.util.ClassUtils; @@ -59,28 +59,27 @@ import org.springframework.util.ClassUtils;
* <p>Tested against Jackson 2.2 and 2.3; compatible with Jackson 2.0 and higher.
*
* @author Sebastien Deleuze
* @author Juergen Hoeller
* @since 4.1.1
* @see #build()
* @see #configure(ObjectMapper)
* @see Jackson2ObjectMapperFactoryBean
*/
public class Jackson2ObjectMapperBuilder {
private static final boolean jackson2XmlPresent =
ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", getClassLoader());
private ObjectMapper objectMapper;
private boolean createXmlMapper = false;
private DateFormat dateFormat;
private JsonInclude.Include serializationInclusion;
private AnnotationIntrospector annotationIntrospector;
private final Map<Class<?>, JsonSerializer<?>>
serializers = new LinkedHashMap<Class<?>, JsonSerializer<?>>();
private PropertyNamingStrategy propertyNamingStrategy;
private JsonInclude.Include serializationInclusion;
private final Map<Class<?>, JsonSerializer<?>> serializers = new LinkedHashMap<Class<?>, JsonSerializer<?>>();
private final Map<Class<?>, JsonDeserializer<?>>
deserializers = new LinkedHashMap<Class<?>, JsonDeserializer<?>>();
private final Map<Class<?>, JsonDeserializer<?>> deserializers = new LinkedHashMap<Class<?>, JsonDeserializer<?>>();
private final Map<Object, Boolean> features = new HashMap<Object, Boolean>();
@ -90,47 +89,35 @@ public class Jackson2ObjectMapperBuilder { @@ -90,47 +89,35 @@ public class Jackson2ObjectMapperBuilder {
private boolean findModulesViaServiceLoader;
private PropertyNamingStrategy propertyNamingStrategy;
private ClassLoader moduleClassLoader = getClass().getClassLoader();
private Jackson2ObjectMapperBuilder() {
}
private Jackson2ObjectMapperBuilder(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
/**
* Obtain a {@link Jackson2ObjectMapperBuilder} instance in order to build an {@link ObjectMapper} instance.
* Obtain a {@link Jackson2ObjectMapperBuilder} instance in order to
* build an {@link ObjectMapper} instance.
*/
public static Jackson2ObjectMapperBuilder json() {
return new Jackson2ObjectMapperBuilder();
}
/**
* Obtain a {@link Jackson2ObjectMapperBuilder} instance in order to build a {@link XmlMapper} instance.
* Obtain a {@link Jackson2ObjectMapperBuilder} instance in order to
* build a {@link XmlMapper} instance.
*/
@SuppressWarnings("unchecked")
public static Jackson2ObjectMapperBuilder xml() {
return new Jackson2ObjectMapperBuilder().createXmlMapper(true);
}
/**
* Obtain a {@link Jackson2ObjectMapperBuilder} in order to customize the {@link ObjectMapper} parameter.
*/
public static Jackson2ObjectMapperBuilder instance(ObjectMapper objectMapper) {
return new Jackson2ObjectMapperBuilder(objectMapper);
}
private static ClassLoader getClassLoader() {
ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
Assert.state(classLoader != null, "No classloader available");
return classLoader;
}
/**
* If set to true and no custom {@link ObjectMapper} has been set, a {@link XmlMapper}
* will be created using its default constructor.
* If set to {@code true}, an {@link XmlMapper} will be created using its
* default constructor. This is only applicable to {@link #build()} calls,
* not to {@link #configure} calls.
*/
public Jackson2ObjectMapperBuilder createXmlMapper(boolean createXmlMapper) {
this.createXmlMapper = createXmlMapper;
@ -167,6 +154,15 @@ public class Jackson2ObjectMapperBuilder { @@ -167,6 +154,15 @@ public class Jackson2ObjectMapperBuilder {
return this;
}
/**
* Specify a {@link com.fasterxml.jackson.databind.PropertyNamingStrategy} to
* configure the {@link ObjectMapper} with.
*/
public Jackson2ObjectMapperBuilder propertyNamingStrategy(PropertyNamingStrategy propertyNamingStrategy) {
this.propertyNamingStrategy = propertyNamingStrategy;
return this;
}
/**
* Set a custom inclusion strategy for serialization.
* @see com.fasterxml.jackson.annotation.JsonInclude.Include
@ -186,8 +182,9 @@ public class Jackson2ObjectMapperBuilder { @@ -186,8 +182,9 @@ public class Jackson2ObjectMapperBuilder {
if (serializers != null) {
for (JsonSerializer<?> serializer : serializers) {
Class<?> handledType = serializer.handledType();
Assert.isTrue(handledType != null && handledType != Object.class,
"Unknown handled type in " + serializer.getClass().getName());
if (handledType == null || handledType == Object.class) {
throw new IllegalArgumentException("Unknown handled type in " + serializer.getClass().getName());
}
this.serializers.put(serializer.handledType(), serializer);
}
}
@ -234,18 +231,18 @@ public class Jackson2ObjectMapperBuilder { @@ -234,18 +231,18 @@ public class Jackson2ObjectMapperBuilder {
}
/**
* Shortcut for {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} option.
* Shortcut for {@link MapperFeature#DEFAULT_VIEW_INCLUSION} option.
*/
public Jackson2ObjectMapperBuilder failOnUnknownProperties(boolean failOnUnknownProperties) {
this.features.put(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
public Jackson2ObjectMapperBuilder defaultViewInclusion(boolean defaultViewInclusion) {
this.features.put(MapperFeature.DEFAULT_VIEW_INCLUSION, defaultViewInclusion);
return this;
}
/**
* Shortcut for {@link MapperFeature#DEFAULT_VIEW_INCLUSION} option.
* Shortcut for {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} option.
*/
public Jackson2ObjectMapperBuilder defaultViewInclusion(boolean defaultViewInclusion) {
this.features.put(MapperFeature.DEFAULT_VIEW_INCLUSION, defaultViewInclusion);
public Jackson2ObjectMapperBuilder failOnUnknownProperties(boolean failOnUnknownProperties) {
this.features.put(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
return this;
}
@ -341,93 +338,104 @@ public class Jackson2ObjectMapperBuilder { @@ -341,93 +338,104 @@ public class Jackson2ObjectMapperBuilder {
}
/**
* Specify a {@link com.fasterxml.jackson.databind.PropertyNamingStrategy} to
* configure the {@link ObjectMapper} with.
* Set the ClassLoader to use for loading Jackson extension modules.
*/
public Jackson2ObjectMapperBuilder propertyNamingStrategy(PropertyNamingStrategy propertyNamingStrategy) {
this.propertyNamingStrategy = propertyNamingStrategy;
public Jackson2ObjectMapperBuilder moduleClassLoader(ClassLoader moduleClassLoader) {
this.moduleClassLoader = moduleClassLoader;
return this;
}
/**
* Build a new {@link T} instance.
* Build a new {@link ObjectMapper} instance.
* <p>Each build operation produces an independent {@link ObjectMapper} instance.
* The builder's settings can get modified, with a subsequent build operation
* then producing a new {@link ObjectMapper} based on the most recent settings.
* @return the newly built ObjectMapper
*/
@SuppressWarnings("unchecked")
public <T extends ObjectMapper> T build() {
if (this.objectMapper == null) {
if(this.createXmlMapper) {
ClassLoader cl = getClassLoader();
try {
Class<? extends ObjectMapper> xmlMapper = (Class<? extends ObjectMapper>)
cl.loadClass("com.fasterxml.jackson.dataformat.xml.XmlMapper");
this.objectMapper = BeanUtils.instantiate(xmlMapper);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException("Could not instantiate XmlMapper, it has not been found on the classpath");
}
ObjectMapper objectMapper;
if (this.createXmlMapper) {
try {
Class<? extends ObjectMapper> xmlMapper = (Class<? extends ObjectMapper>)
ClassUtils.forName("com.fasterxml.jackson.dataformat.xml.XmlMapper", this.moduleClassLoader);
objectMapper = BeanUtils.instantiate(xmlMapper);
}
else {
this.objectMapper = new ObjectMapper();
catch (ClassNotFoundException ex) {
throw new IllegalStateException("Could not instantiate XmlMapper - not found on classpath");
}
}
else {
objectMapper = new ObjectMapper();
}
configure(objectMapper);
return (T) objectMapper;
}
/**
* Configure an existing {@link ObjectMapper} instance with this builder's
* settings. This can be applied to any number of {@code ObjectMappers}.
* @param objectMapper the ObjectMapper to configure
*/
public void configure(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "ObjectMapper must not be null");
if (this.dateFormat != null) {
this.objectMapper.setDateFormat(this.dateFormat);
objectMapper.setDateFormat(this.dateFormat);
}
if (this.annotationIntrospector != null) {
this.objectMapper.setAnnotationIntrospector(this.annotationIntrospector);
objectMapper.setAnnotationIntrospector(this.annotationIntrospector);
}
if (this.serializationInclusion != null) {
this.objectMapper.setSerializationInclusion(this.serializationInclusion);
objectMapper.setSerializationInclusion(this.serializationInclusion);
}
if (!this.serializers.isEmpty() || !this.deserializers.isEmpty()) {
SimpleModule module = new SimpleModule();
addSerializers(module);
addDeserializers(module);
this.objectMapper.registerModule(module);
objectMapper.registerModule(module);
}
if(!features.containsKey(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
configureFeature(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
if (!this.features.containsKey(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
configureFeature(objectMapper, MapperFeature.DEFAULT_VIEW_INCLUSION, false);
}
if(!features.containsKey(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
configureFeature(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
if (!this.features.containsKey(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
configureFeature(objectMapper, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
for (Object feature : this.features.keySet()) {
configureFeature(feature, this.features.get(feature));
configureFeature(objectMapper, feature, this.features.get(feature));
}
if (this.modules != null) {
// Complete list of modules given
for (Module module : this.modules) {
// Using Jackson 2.0+ registerModule method, not Jackson 2.2+ registerModules
this.objectMapper.registerModule(module);
objectMapper.registerModule(module);
}
}
else {
// Combination of modules by class names specified and class presence in the classpath
if (this.modulesToInstall != null) {
for (Class<? extends Module> module : this.modulesToInstall) {
this.objectMapper.registerModule(BeanUtils.instantiate(module));
objectMapper.registerModule(BeanUtils.instantiate(module));
}
}
if (this.findModulesViaServiceLoader) {
// Jackson 2.2+
this.objectMapper.registerModules(ObjectMapper.findModules(getClassLoader()));
objectMapper.registerModules(ObjectMapper.findModules(this.moduleClassLoader));
}
else {
registerWellKnownModulesIfAvailable();
registerWellKnownModulesIfAvailable(objectMapper);
}
}
if (this.propertyNamingStrategy != null) {
this.objectMapper.setPropertyNamingStrategy(this.propertyNamingStrategy);
objectMapper.setPropertyNamingStrategy(this.propertyNamingStrategy);
}
return (T)this.objectMapper;
}
@SuppressWarnings("unchecked")
@ -444,21 +452,21 @@ public class Jackson2ObjectMapperBuilder { @@ -444,21 +452,21 @@ public class Jackson2ObjectMapperBuilder {
}
}
private void configureFeature(Object feature, boolean enabled) {
private void configureFeature(ObjectMapper objectMapper, Object feature, boolean enabled) {
if (feature instanceof JsonParser.Feature) {
this.objectMapper.configure((JsonParser.Feature) feature, enabled);
objectMapper.configure((JsonParser.Feature) feature, enabled);
}
else if (feature instanceof JsonGenerator.Feature) {
this.objectMapper.configure((JsonGenerator.Feature) feature, enabled);
objectMapper.configure((JsonGenerator.Feature) feature, enabled);
}
else if (feature instanceof SerializationFeature) {
this.objectMapper.configure((SerializationFeature) feature, enabled);
objectMapper.configure((SerializationFeature) feature, enabled);
}
else if (feature instanceof DeserializationFeature) {
this.objectMapper.configure((DeserializationFeature) feature, enabled);
objectMapper.configure((DeserializationFeature) feature, enabled);
}
else if (feature instanceof MapperFeature) {
this.objectMapper.configure((MapperFeature) feature, enabled);
objectMapper.configure((MapperFeature) feature, enabled);
}
else {
throw new FatalBeanException("Unknown feature class: " + feature.getClass().getName());
@ -466,25 +474,24 @@ public class Jackson2ObjectMapperBuilder { @@ -466,25 +474,24 @@ public class Jackson2ObjectMapperBuilder {
}
@SuppressWarnings("unchecked")
private void registerWellKnownModulesIfAvailable() {
ClassLoader cl = getClassLoader();
private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) {
// Java 8 java.time package present?
if (ClassUtils.isPresent("java.time.LocalDate", cl)) {
if (ClassUtils.isPresent("java.time.LocalDate", this.moduleClassLoader)) {
try {
Class<? extends Module> jsr310Module = (Class<? extends Module>)
cl.loadClass("com.fasterxml.jackson.datatype.jsr310.JSR310Module");
this.objectMapper.registerModule(BeanUtils.instantiate(jsr310Module));
ClassUtils.forName("com.fasterxml.jackson.datatype.jsr310.JSR310Module", this.moduleClassLoader);
objectMapper.registerModule(BeanUtils.instantiate(jsr310Module));
}
catch (ClassNotFoundException ex) {
// jackson-datatype-jsr310 not available
}
}
// Joda-Time present?
if (ClassUtils.isPresent("org.joda.time.LocalDate", cl)) {
if (ClassUtils.isPresent("org.joda.time.LocalDate", this.moduleClassLoader)) {
try {
Class<? extends Module> jodaModule = (Class<? extends Module>)
cl.loadClass("com.fasterxml.jackson.datatype.joda.JodaModule");
this.objectMapper.registerModule(BeanUtils.instantiate(jodaModule));
ClassUtils.forName("com.fasterxml.jackson.datatype.joda.JodaModule", this.moduleClassLoader);
objectMapper.registerModule(BeanUtils.instantiate(jodaModule));
}
catch (ClassNotFoundException ex) {
// jackson-datatype-joda not available

272
spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.java

@ -18,15 +18,10 @@ package org.springframework.http.converter.json; @@ -18,15 +18,10 @@ package org.springframework.http.converter.json;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
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.AnnotationIntrospector;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
@ -36,16 +31,11 @@ import com.fasterxml.jackson.databind.Module; @@ -36,16 +31,11 @@ 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.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* A {@link FactoryBean} for creating a Jackson 2.x {@link ObjectMapper} (default) or
@ -54,8 +44,8 @@ import org.springframework.util.ClassUtils; @@ -54,8 +44,8 @@ import org.springframework.util.ClassUtils;
*
* <p>It customizes Jackson defaults properties with the following ones:
* <ul>
* <li>{@link MapperFeature#DEFAULT_VIEW_INCLUSION} is disabled</li>
* <li>{@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} is disabled</li>
* <li>{@link MapperFeature#DEFAULT_VIEW_INCLUSION} is disabled</li>
* <li>{@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} is disabled</li>
* </ul>
*
* <p>Example usage with
@ -132,31 +122,9 @@ import org.springframework.util.ClassUtils; @@ -132,31 +122,9 @@ import org.springframework.util.ClassUtils;
*/
public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper>, BeanClassLoaderAware, InitializingBean {
private ObjectMapper objectMapper;
private boolean createXmlMapper = false;
private DateFormat dateFormat;
private JsonInclude.Include serializationInclusion;
private AnnotationIntrospector annotationIntrospector;
private final Map<Class<?>, JsonSerializer<?>> serializers = new LinkedHashMap<Class<?>, JsonSerializer<?>>();
private final Map<Class<?>, JsonDeserializer<?>> deserializers = new LinkedHashMap<Class<?>, JsonDeserializer<?>>();
private final Map<Object, Boolean> features = new HashMap<Object, Boolean>();
private List<Module> modules;
private Class<? extends Module>[] modulesToInstall;
private boolean findModulesViaServiceLoader;
private final Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
private PropertyNamingStrategy propertyNamingStrategy;
private ClassLoader beanClassLoader;
private ObjectMapper objectMapper;
/**
@ -170,9 +138,10 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -170,9 +138,10 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
/**
* If set to true and no custom {@link ObjectMapper} has been set, a {@link XmlMapper}
* will be created using its default constructor.
* @since 4.1
*/
public void setCreateXmlMapper(boolean createXmlMapper) {
this.createXmlMapper = createXmlMapper;
this.builder.createXmlMapper(createXmlMapper);
}
/**
@ -182,7 +151,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -182,7 +151,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
* @see #setSimpleDateFormat(String)
*/
public void setDateFormat(DateFormat dateFormat) {
this.dateFormat = dateFormat;
this.builder.dateFormat(dateFormat);
}
/**
@ -192,14 +161,23 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -192,14 +161,23 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
* @see #setDateFormat(DateFormat)
*/
public void setSimpleDateFormat(String format) {
this.dateFormat = new SimpleDateFormat(format);
this.builder.simpleDateFormat(format);
}
/**
* Set an {@link AnnotationIntrospector} for both serialization and deserialization.
*/
public void setAnnotationIntrospector(AnnotationIntrospector annotationIntrospector) {
this.annotationIntrospector = annotationIntrospector;
this.builder.annotationIntrospector(annotationIntrospector);
}
/**
* Specify a {@link com.fasterxml.jackson.databind.PropertyNamingStrategy} to
* configure the {@link ObjectMapper} with.
* @since 4.0.2
*/
public void setPropertyNamingStrategy(PropertyNamingStrategy propertyNamingStrategy) {
this.builder.propertyNamingStrategy(propertyNamingStrategy);
}
/**
@ -207,7 +185,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -207,7 +185,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
* @see com.fasterxml.jackson.annotation.JsonInclude.Include
*/
public void setSerializationInclusion(JsonInclude.Include serializationInclusion) {
this.serializationInclusion = serializationInclusion;
this.builder.serializationInclusion(serializationInclusion);
}
/**
@ -217,14 +195,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -217,14 +195,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
* @see #setSerializersByType(Map)
*/
public void setSerializers(JsonSerializer<?>... serializers) {
if (serializers != null) {
for (JsonSerializer<?> serializer : serializers) {
Class<?> handledType = serializer.handledType();
Assert.isTrue(handledType != null && handledType != Object.class,
"Unknown handled type in " + serializer.getClass().getName());
this.serializers.put(serializer.handledType(), serializer);
}
}
this.builder.serializers(serializers);
}
/**
@ -232,25 +203,21 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -232,25 +203,21 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
* @see #setSerializers(JsonSerializer...)
*/
public void setSerializersByType(Map<Class<?>, JsonSerializer<?>> serializers) {
if (serializers != null) {
this.serializers.putAll(serializers);
}
this.builder.serializersByType(serializers);
}
/**
* Configure custom deserializers for the given types.
*/
public void setDeserializersByType(Map<Class<?>, JsonDeserializer<?>> deserializers) {
if (deserializers != null) {
this.deserializers.putAll(deserializers);
}
this.builder.deserializersByType(deserializers);
}
/**
* Shortcut for {@link MapperFeature#AUTO_DETECT_FIELDS} option.
*/
public void setAutoDetectFields(boolean autoDetectFields) {
this.features.put(MapperFeature.AUTO_DETECT_FIELDS, autoDetectFields);
this.builder.autoDetectFields(autoDetectFields);
}
/**
@ -258,36 +225,37 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -258,36 +225,37 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
* {@link MapperFeature#AUTO_DETECT_GETTERS} option.
*/
public void setAutoDetectGettersSetters(boolean autoDetectGettersSetters) {
this.features.put(MapperFeature.AUTO_DETECT_GETTERS, autoDetectGettersSetters);
this.features.put(MapperFeature.AUTO_DETECT_SETTERS, autoDetectGettersSetters);
this.builder.autoDetectGettersSetters(autoDetectGettersSetters);
}
/**
* Shortcut for {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} option.
* Shortcut for {@link MapperFeature#DEFAULT_VIEW_INCLUSION} option.
* @since 4.1
*/
public void setFailOnUnknownProperties(boolean failOnUnknownProperties) {
this.features.put(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
public void setDefaultViewInclusion(boolean defaultViewInclusion) {
this.builder.defaultViewInclusion(defaultViewInclusion);
}
/**
* Shortcut for {@link MapperFeature#DEFAULT_VIEW_INCLUSION} option.
* Shortcut for {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} option.
* @since 4.1.1
*/
public void setDefaultViewInclusion(boolean defaultViewInclusion) {
this.features.put(MapperFeature.DEFAULT_VIEW_INCLUSION, defaultViewInclusion);
public void setFailOnUnknownProperties(boolean failOnUnknownProperties) {
this.builder.failOnUnknownProperties(failOnUnknownProperties);
}
/**
* Shortcut for {@link SerializationFeature#FAIL_ON_EMPTY_BEANS} option.
*/
public void setFailOnEmptyBeans(boolean failOnEmptyBeans) {
this.features.put(SerializationFeature.FAIL_ON_EMPTY_BEANS, failOnEmptyBeans);
this.builder.failOnEmptyBeans(failOnEmptyBeans);
}
/**
* Shortcut for {@link SerializationFeature#INDENT_OUTPUT} option.
*/
public void setIndentOutput(boolean indentOutput) {
this.features.put(SerializationFeature.INDENT_OUTPUT, indentOutput);
this.builder.indentOutput(indentOutput);
}
/**
@ -299,11 +267,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -299,11 +267,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
* @see com.fasterxml.jackson.databind.MapperFeature
*/
public void setFeaturesToEnable(Object... featuresToEnable) {
if (featuresToEnable != null) {
for (Object feature : featuresToEnable) {
this.features.put(feature, Boolean.TRUE);
}
}
this.builder.featuresToEnable(featuresToEnable);
}
/**
@ -315,11 +279,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -315,11 +279,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
* @see com.fasterxml.jackson.databind.MapperFeature
*/
public void setFeaturesToDisable(Object... featuresToDisable) {
if (featuresToDisable != null) {
for (Object feature : featuresToDisable) {
this.features.put(feature, Boolean.FALSE);
}
}
this.builder.featuresToDisable(featuresToDisable);
}
/**
@ -333,7 +293,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -333,7 +293,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
* @see com.fasterxml.jackson.databind.Module
*/
public void setModules(List<Module> modules) {
this.modules = new LinkedList<Module>(modules);
this.builder.modules(modules);
}
/**
@ -347,7 +307,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -347,7 +307,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
* @see com.fasterxml.jackson.databind.Module
*/
public void setModulesToInstall(Class<? extends Module>... modules) {
this.modulesToInstall = modules;
this.builder.modulesToInstall(modules);
}
/**
@ -360,165 +320,23 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -360,165 +320,23 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
* @see com.fasterxml.jackson.databind.ObjectMapper#findModules()
*/
public void setFindModulesViaServiceLoader(boolean findModules) {
this.findModulesViaServiceLoader = findModules;
}
/**
* Specify a {@link com.fasterxml.jackson.databind.PropertyNamingStrategy} to
* configure the {@link ObjectMapper} with.
* @since 4.0.2
*/
public void setPropertyNamingStrategy(PropertyNamingStrategy propertyNamingStrategy) {
this.propertyNamingStrategy = propertyNamingStrategy;
this.builder.findModulesViaServiceLoader(findModules);
}
@Override
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
this.builder.moduleClassLoader(beanClassLoader);
}
@Override
@SuppressWarnings("unchecked")
public void afterPropertiesSet() {
if (this.objectMapper == null) {
if(this.createXmlMapper) {
ClassLoader cl = this.beanClassLoader;
if (cl == null) {
cl = getClass().getClassLoader();
}
try {
Class<? extends ObjectMapper> xmlMapper = (Class<? extends ObjectMapper>)
cl.loadClass("com.fasterxml.jackson.dataformat.xml.XmlMapper");
this.objectMapper = BeanUtils.instantiate(xmlMapper);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException("Could not instantiate XmlMapper", ex);
}
}
else {
this.objectMapper = new ObjectMapper();
}
}
if (this.dateFormat != null) {
this.objectMapper.setDateFormat(this.dateFormat);
}
if (this.annotationIntrospector != null) {
this.objectMapper.setAnnotationIntrospector(this.annotationIntrospector);
}
if (this.serializationInclusion != null) {
this.objectMapper.setSerializationInclusion(this.serializationInclusion);
}
if (!this.serializers.isEmpty() || !this.deserializers.isEmpty()) {
SimpleModule module = new SimpleModule();
addSerializers(module);
addDeserializers(module);
this.objectMapper.registerModule(module);
}
if(!features.containsKey(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
configureFeature(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
}
if(!features.containsKey(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
configureFeature(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
for (Object feature : this.features.keySet()) {
configureFeature(feature, this.features.get(feature));
}
if (this.modules != null) {
// Complete list of modules given
for (Module module : this.modules) {
// Using Jackson 2.0+ registerModule method, not Jackson 2.2+ registerModules
this.objectMapper.registerModule(module);
}
if (this.objectMapper != null) {
this.builder.configure(this.objectMapper);
}
else {
// Combination of modules by class names specified and class presence in the classpath
if (this.modulesToInstall != null) {
for (Class<? extends Module> module : this.modulesToInstall) {
this.objectMapper.registerModule(BeanUtils.instantiate(module));
}
}
if (this.findModulesViaServiceLoader) {
// Jackson 2.2+
this.objectMapper.registerModules(ObjectMapper.findModules(this.beanClassLoader));
}
else {
registerWellKnownModulesIfAvailable();
}
}
if (this.propertyNamingStrategy != null) {
this.objectMapper.setPropertyNamingStrategy(this.propertyNamingStrategy);
}
}
@SuppressWarnings("unchecked")
private <T> void addSerializers(SimpleModule module) {
for (Class<?> type : this.serializers.keySet()) {
module.addSerializer((Class<? extends T>) type, (JsonSerializer<T>) this.serializers.get(type));
}
}
@SuppressWarnings("unchecked")
private <T> void addDeserializers(SimpleModule module) {
for (Class<?> type : this.deserializers.keySet()) {
module.addDeserializer((Class<T>) type, (JsonDeserializer<? extends T>) this.deserializers.get(type));
}
}
private void configureFeature(Object feature, boolean enabled) {
if (feature instanceof JsonParser.Feature) {
this.objectMapper.configure((JsonParser.Feature) feature, enabled);
}
else if (feature instanceof JsonGenerator.Feature) {
this.objectMapper.configure((JsonGenerator.Feature) feature, enabled);
}
else if (feature instanceof SerializationFeature) {
this.objectMapper.configure((SerializationFeature) feature, enabled);
}
else if (feature instanceof DeserializationFeature) {
this.objectMapper.configure((DeserializationFeature) feature, enabled);
}
else if (feature instanceof MapperFeature) {
this.objectMapper.configure((MapperFeature) feature, enabled);
}
else {
throw new FatalBeanException("Unknown feature class: " + feature.getClass().getName());
}
}
@SuppressWarnings("unchecked")
private void registerWellKnownModulesIfAvailable() {
ClassLoader cl = this.beanClassLoader;
if (cl == null) {
cl = getClass().getClassLoader();
}
// Java 8 java.time package present?
if (ClassUtils.isPresent("java.time.LocalDate", cl)) {
try {
Class<? extends Module> jsr310Module = (Class<? extends Module>)
cl.loadClass("com.fasterxml.jackson.datatype.jsr310.JSR310Module");
this.objectMapper.registerModule(BeanUtils.instantiate(jsr310Module));
}
catch (ClassNotFoundException ex) {
// jackson-datatype-jsr310 not available
}
}
// Joda-Time present?
if (ClassUtils.isPresent("org.joda.time.LocalDate", cl)) {
try {
Class<? extends Module> jodaModule = (Class<? extends Module>)
cl.loadClass("com.fasterxml.jackson.datatype.joda.JodaModule");
this.objectMapper.registerModule(BeanUtils.instantiate(jodaModule));
}
catch (ClassNotFoundException ex) {
// jackson-datatype-joda not available
}
this.objectMapper = this.builder.build();
}
}
@ -533,7 +351,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper @@ -533,7 +351,7 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
@Override
public Class<?> getObjectType() {
return (this.objectMapper != null) ? this.objectMapper.getClass() : null;
return (this.objectMapper != null ? this.objectMapper.getClass() : null);
}
@Override

14
spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilderTests.java

@ -51,7 +51,6 @@ import org.junit.Test; @@ -51,7 +51,6 @@ 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}.
@ -195,18 +194,16 @@ public class Jackson2ObjectMapperBuilderTests { @@ -195,18 +194,16 @@ public class Jackson2ObjectMapperBuilderTests {
public void completeSetup() {
NopAnnotationIntrospector annotationIntrospector = NopAnnotationIntrospector.instance;
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.instance(new ObjectMapper());
Map<Class<?>, JsonDeserializer<?>>
deserializers = new HashMap<Class<?>, JsonDeserializer<?>>();
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();
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
builder.serializers(serializer1);
builder.serializersByType(Collections
.<Class<?>, JsonSerializer<?>>singletonMap(Boolean.class, serializer2));
builder.serializersByType(Collections.<Class<?>, JsonSerializer<?>>singletonMap(Boolean.class, serializer2));
builder.deserializersByType(deserializers);
builder.annotationIntrospector(annotationIntrospector);
@ -221,7 +218,8 @@ public class Jackson2ObjectMapperBuilderTests { @@ -221,7 +218,8 @@ public class Jackson2ObjectMapperBuilderTests {
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
ObjectMapper objectMapper = builder.build();
ObjectMapper objectMapper = new ObjectMapper();
builder.configure(objectMapper);
assertTrue(getSerializerFactoryConfig(objectMapper).hasSerializers());
assertTrue(getDeserializerFactoryConfig(objectMapper).hasDeserializers());

2
spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBeanTests.java

@ -65,11 +65,13 @@ public class Jackson2ObjectMapperFactoryBeanTests { @@ -65,11 +65,13 @@ public class Jackson2ObjectMapperFactoryBeanTests {
private Jackson2ObjectMapperFactoryBean factory;
@Before
public void setUp() {
factory = new Jackson2ObjectMapperFactoryBean();
}
@Test
public void settersWithNullValues() {
// Should not crash:

Loading…
Cancel
Save