Jackson2ObjectMapperFactoryBean builds on revised Jackson2ObjectMapperBuilder now
Issue: SPR-12243
This commit is contained in:
@@ -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;
|
||||
* <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 final Map<Class<?>, JsonDeserializer<?>>
|
||||
deserializers = new LinkedHashMap<Class<?>, JsonDeserializer<?>>();
|
||||
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<Object, Boolean> features = new HashMap<Object, Boolean>();
|
||||
|
||||
@@ -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 {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -233,14 +230,6 @@ public class Jackson2ObjectMapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} option.
|
||||
*/
|
||||
public Jackson2ObjectMapperBuilder failOnUnknownProperties(boolean failOnUnknownProperties) {
|
||||
this.features.put(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for {@link MapperFeature#DEFAULT_VIEW_INCLUSION} option.
|
||||
*/
|
||||
@@ -249,6 +238,14 @@ public class Jackson2ObjectMapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} option.
|
||||
*/
|
||||
public Jackson2ObjectMapperBuilder failOnUnknownProperties(boolean failOnUnknownProperties) {
|
||||
this.features.put(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for {@link SerializationFeature#FAIL_ON_EMPTY_BEANS} option.
|
||||
*/
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
@@ -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;
|
||||
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;
|
||||
*
|
||||
* <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,32 +122,10 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper>, BeanClassLoaderAware, InitializingBean {
|
||||
|
||||
private final Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
|
||||
|
||||
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 PropertyNamingStrategy propertyNamingStrategy;
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
|
||||
/**
|
||||
* Set the {@link ObjectMapper} instance to use. If not set, the {@link 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
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
* {@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} option.
|
||||
*/
|
||||
public void setFailOnUnknownProperties(boolean failOnUnknownProperties) {
|
||||
this.features.put(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
|
||||
this.builder.autoDetectGettersSetters(autoDetectGettersSetters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for {@link MapperFeature#DEFAULT_VIEW_INCLUSION} option.
|
||||
* @since 4.1
|
||||
*/
|
||||
public void setDefaultViewInclusion(boolean defaultViewInclusion) {
|
||||
this.features.put(MapperFeature.DEFAULT_VIEW_INCLUSION, defaultViewInclusion);
|
||||
this.builder.defaultViewInclusion(defaultViewInclusion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} option.
|
||||
* @since 4.1.1
|
||||
*/
|
||||
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
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return (this.objectMapper != null) ? this.objectMapper.getClass() : null;
|
||||
return (this.objectMapper != null ? this.objectMapper.getClass() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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 {
|
||||
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 {
|
||||
|
||||
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());
|
||||
|
||||
@@ -65,11 +65,13 @@ public class Jackson2ObjectMapperFactoryBeanTests {
|
||||
|
||||
private Jackson2ObjectMapperFactoryBean factory;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
factory = new Jackson2ObjectMapperFactoryBean();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void settersWithNullValues() {
|
||||
// Should not crash:
|
||||
|
||||
Reference in New Issue
Block a user