@ -20,8 +20,8 @@ import java.lang.reflect.Field;
import java.text.DateFormat ;
import java.text.SimpleDateFormat ;
import java.util.Collection ;
import java.util.Locale ;
import java.util.List ;
import java.util.Locale ;
import java.util.Map ;
import java.util.Map.Entry ;
import java.util.TimeZone ;
@ -42,7 +42,6 @@ import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils ;
import org.springframework.beans.factory.BeanFactoryUtils ;
import org.springframework.beans.factory.ListableBeanFactory ;
import org.springframework.beans.factory.ObjectProvider ;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass ;
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava ;
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.JavaVersion ;
@ -53,7 +52,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean ;
import org.springframework.context.annotation.Configuration ;
import org.springframework.context.annotation.Primary ;
import org.springframework.core. annotation.AnnotationAwareOrderComparator ;
import org.springframework.core. Ordered ;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder ;
import org.springframework.util.Assert ;
import org.springframework.util.ClassUtils ;
@ -88,30 +87,13 @@ public class JacksonAutoConfiguration {
@ConditionalOnClass ( { ObjectMapper . class , Jackson2ObjectMapperBuilder . class } )
static class JacksonObjectMapperConfiguration {
private final List < Jackson2ObjectMapperBuilderCustomizer > builderCustomizers ;
JacksonObjectMapperConfiguration (
ObjectProvider < List < Jackson2ObjectMapperBuilderCustomizer > > builderCustomizersProvider ) {
this . builderCustomizers = builderCustomizersProvider . getIfAvailable ( ) ;
}
@Bean
@Primary
@ConditionalOnMissingBean ( ObjectMapper . class )
public ObjectMapper jacksonObjectMapper ( Jackson2ObjectMapperBuilder builder ) {
customize ( builder ) ;
return builder . createXmlMapper ( false ) . build ( ) ;
}
private void customize ( Jackson2ObjectMapperBuilder builder ) {
if ( this . builderCustomizers ! = null ) {
AnnotationAwareOrderComparator . sort ( this . builderCustomizers ) ;
for ( Jackson2ObjectMapperBuilderCustomizer customizer : this . builderCustomizers ) {
customizer . customize ( builder ) ;
}
}
}
}
@Configuration
@ -180,137 +162,189 @@ public class JacksonAutoConfiguration {
@Configuration
@ConditionalOnClass ( { ObjectMapper . class , Jackson2ObjectMapperBuilder . class } )
@EnableConfigurationProperties ( JacksonProperties . class )
static class JacksonObjectMapperBuilderConfiguration {
private final ApplicationContext applicationContext ;
private final JacksonProperties jacksonProperties ;
JacksonObjectMapperBuilderConfiguration ( ApplicationContext applicationContext ,
JacksonProperties jacksonProperties ) {
JacksonProperties jacksonProperties ,
List < Jackson2ObjectMapperBuilderCustomizer > customizers ) {
this . applicationContext = applicationContext ;
this . jacksonProperties = jacksonProperties ;
}
@Bean
@ConditionalOnMissingBean ( Jackson2ObjectMapperBuilder . class )
public Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder ( ) {
public Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder (
List < Jackson2ObjectMapperBuilderCustomizer > customizers ) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder ( ) ;
builder . applicationContext ( this . applicationContext ) ;
if ( this . jacksonProperties . getDefaultPropertyInclusion ( ) ! = null ) {
builder . serializationInclusion (
this . jacksonProperties . getDefaultPropertyInclusion ( ) ) ;
}
if ( this . jacksonProperties . getTimeZone ( ) ! = null ) {
builder . timeZone ( this . jacksonProperties . getTimeZone ( ) ) ;
}
configureFeatures ( builder , this . jacksonProperties . getDeserialization ( ) ) ;
configureFeatures ( builder , this . jacksonProperties . getSerialization ( ) ) ;
configureFeatures ( builder , this . jacksonProperties . getMapper ( ) ) ;
configureFeatures ( builder , this . jacksonProperties . getParser ( ) ) ;
configureFeatures ( builder , this . jacksonProperties . getGenerator ( ) ) ;
configureDateFormat ( builder ) ;
configurePropertyNamingStrategy ( builder ) ;
configureModules ( builder ) ;
configureLocale ( builder ) ;
customize ( builder , customizers ) ;
return builder ;
}
private void configureFeatures ( Jackson2ObjectMapperBuilder builder ,
Map < ? , Boolean > features ) {
for ( Entry < ? , Boolean > entry : features . entrySet ( ) ) {
if ( entry . getValue ( ) ! = null & & entry . getValue ( ) ) {
builder . featuresToEnable ( entry . getKey ( ) ) ;
}
else {
builder . featuresToDisable ( entry . getKey ( ) ) ;
}
private void customize ( Jackson2ObjectMapperBuilder builder ,
List < Jackson2ObjectMapperBuilderCustomizer > customizers ) {
for ( Jackson2ObjectMapperBuilderCustomizer customizer : customizers ) {
customizer . customize ( builder ) ;
}
}
private void configureDateFormat ( Jackson2ObjectMapperBuilder builder ) {
// We support a fully qualified class name extending DateFormat or a date
// pattern string value
String dateFormat = this . jacksonProperties . getDateFormat ( ) ;
if ( dateFormat ! = null ) {
try {
Class < ? > dateFormatClass = ClassUtils . forName ( dateFormat , null ) ;
builder . dateFormat (
( DateFormat ) BeanUtils . instantiateClass ( dateFormatClass ) ) ;
}
@Configuration
@ConditionalOnClass ( { ObjectMapper . class , Jackson2ObjectMapperBuilder . class } )
@EnableConfigurationProperties ( JacksonProperties . class )
static class Jackson2ObjectMapperBuilderCustomizerConfiguration {
@Bean
public StandardJackson2ObjectMapperBuilderCustomizer standardJacksonObjectMapperBuilderCustomizer (
ApplicationContext applicationContext ,
JacksonProperties jacksonProperties ) {
return new StandardJackson2ObjectMapperBuilderCustomizer ( applicationContext ,
jacksonProperties ) ;
}
private static final class StandardJackson2ObjectMapperBuilderCustomizer
implements Jackson2ObjectMapperBuilderCustomizer , Ordered {
private final ApplicationContext applicationContext ;
private final JacksonProperties jacksonProperties ;
StandardJackson2ObjectMapperBuilderCustomizer (
ApplicationContext applicationContext ,
JacksonProperties jacksonProperties ) {
this . applicationContext = applicationContext ;
this . jacksonProperties = jacksonProperties ;
}
@Override
public int getOrder ( ) {
return 0 ;
}
@Override
public void customize ( Jackson2ObjectMapperBuilder builder ) {
if ( this . jacksonProperties . getDefaultPropertyInclusion ( ) ! = null ) {
builder . serializationInclusion (
this . jacksonProperties . getDefaultPropertyInclusion ( ) ) ;
}
catch ( ClassNotFoundException ex ) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat ( dateFormat ) ;
// Since Jackson 2.6.3 we always need to set a TimeZone (see gh-4170)
// If none in our properties fallback to the Jackson's default
TimeZone timeZone = this . jacksonProperties . getTimeZone ( ) ;
if ( timeZone = = null ) {
timeZone = new ObjectMapper ( ) . getSerializationConfig ( )
. getTimeZone ( ) ;
if ( this . jacksonProperties . getTimeZone ( ) ! = null ) {
builder . timeZone ( this . jacksonProperties . getTimeZone ( ) ) ;
}
configureFeatures ( builder , this . jacksonProperties . getDeserialization ( ) ) ;
configureFeatures ( builder , this . jacksonProperties . getSerialization ( ) ) ;
configureFeatures ( builder , this . jacksonProperties . getMapper ( ) ) ;
configureFeatures ( builder , this . jacksonProperties . getParser ( ) ) ;
configureFeatures ( builder , this . jacksonProperties . getGenerator ( ) ) ;
configureDateFormat ( builder ) ;
configurePropertyNamingStrategy ( builder ) ;
configureModules ( builder ) ;
configureLocale ( builder ) ;
}
private void configureFeatures ( Jackson2ObjectMapperBuilder builder ,
Map < ? , Boolean > features ) {
for ( Entry < ? , Boolean > entry : features . entrySet ( ) ) {
if ( entry . getValue ( ) ! = null & & entry . getValue ( ) ) {
builder . featuresToEnable ( entry . getKey ( ) ) ;
}
else {
builder . featuresToDisable ( entry . getKey ( ) ) ;
}
simpleDateFormat . setTimeZone ( timeZone ) ;
builder . dateFormat ( simpleDateFormat ) ;
}
}
}
private void configurePropertyNamingStrategy (
Jackson2ObjectMapperBuilder builder ) {
// We support a fully qualified class name extending Jackson's
// PropertyNamingStrategy or a string value corresponding to the constant
// names in PropertyNamingStrategy which hold default provided implementations
String strategy = this . jacksonProperties . getPropertyNamingStrategy ( ) ;
if ( strategy ! = null ) {
try {
configurePropertyNamingStrategyClass ( builder ,
ClassUtils . forName ( strategy , null ) ) ;
private void configureDateFormat ( Jackson2ObjectMapperBuilder builder ) {
// We support a fully qualified class name extending DateFormat or a date
// pattern string value
String dateFormat = this . jacksonProperties . getDateFormat ( ) ;
if ( dateFormat ! = null ) {
try {
Class < ? > dateFormatClass = ClassUtils . forName ( dateFormat , null ) ;
builder . dateFormat (
( DateFormat ) BeanUtils . instantiateClass ( dateFormatClass ) ) ;
}
catch ( ClassNotFoundException ex ) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat (
dateFormat ) ;
// Since Jackson 2.6.3 we always need to set a TimeZone (see
// gh-4170). If none in our properties fallback to the Jackson's
// default
TimeZone timeZone = this . jacksonProperties . getTimeZone ( ) ;
if ( timeZone = = null ) {
timeZone = new ObjectMapper ( ) . getSerializationConfig ( )
. getTimeZone ( ) ;
}
simpleDateFormat . setTimeZone ( timeZone ) ;
builder . dateFormat ( simpleDateFormat ) ;
}
}
catch ( ClassNotFoundException ex ) {
configurePropertyNamingStrategyField ( builder , strategy ) ;
}
private void configurePropertyNamingStrategy (
Jackson2ObjectMapperBuilder builder ) {
// We support a fully qualified class name extending Jackson's
// PropertyNamingStrategy or a string value corresponding to the constant
// names in PropertyNamingStrategy which hold default provided
// implementations
String strategy = this . jacksonProperties . getPropertyNamingStrategy ( ) ;
if ( strategy ! = null ) {
try {
configurePropertyNamingStrategyClass ( builder ,
ClassUtils . forName ( strategy , null ) ) ;
}
catch ( ClassNotFoundException ex ) {
configurePropertyNamingStrategyField ( builder , strategy ) ;
}
}
}
}
private void configurePropertyNamingStrategyClass (
Jackson2ObjectMapperBuilder builder ,
Class < ? > propertyNamingStrategyClass ) {
builder . propertyNamingStrategy ( ( PropertyNamingStrategy ) BeanUtils
. instantiateClass ( propertyNamingStrategyClass ) ) ;
}
private void configurePropertyNamingStrategyClass (
Jackson2ObjectMapperBuilder builder ,
Class < ? > propertyNamingStrategyClass ) {
builder . propertyNamingStrategy ( ( PropertyNamingStrategy ) BeanUtils
. instantiateClass ( propertyNamingStrategyClass ) ) ;
}
private void configurePropertyNamingStrategyField (
Jackson2ObjectMapperBuilder builder , String fieldName ) {
// Find the field (this way we automatically support new constants
// that may be added by Jackson in the future)
Field field = ReflectionUtils . findField ( PropertyNamingStrategy . class ,
fieldName , PropertyNamingStrategy . class ) ;
Assert . notNull ( field , "Constant named '" + fieldName + "' not found on "
+ PropertyNamingStrategy . class . getName ( ) ) ;
try {
builder . propertyNamingStrategy ( ( PropertyNamingStrategy ) field . get ( null ) ) ;
private void configurePropertyNamingStrategyField (
Jackson2ObjectMapperBuilder builder , String fieldName ) {
// Find the field (this way we automatically support new constants
// that may be added by Jackson in the future)
Field field = ReflectionUtils . findField ( PropertyNamingStrategy . class ,
fieldName , PropertyNamingStrategy . class ) ;
Assert . notNull ( field , "Constant named '" + fieldName + "' not found on "
+ PropertyNamingStrategy . class . getName ( ) ) ;
try {
builder . propertyNamingStrategy (
( PropertyNamingStrategy ) field . get ( null ) ) ;
}
catch ( Exception ex ) {
throw new IllegalStateException ( ex ) ;
}
}
catch ( Exception ex ) {
throw new IllegalStateException ( ex ) ;
private void configureModules ( Jackson2ObjectMapperBuilder builder ) {
Collection < Module > moduleBeans = getBeans ( this . applicationContext ,
Module . class ) ;
builder . modulesToInstall (
moduleBeans . toArray ( new Module [ moduleBeans . size ( ) ] ) ) ;
}
}
private void configureModules ( Jackson2ObjectMapperBuilder builder ) {
Collection < Module > moduleBeans = getBeans ( this . applicationContext ,
Module . class ) ;
builder . modulesToInstall ( moduleBeans . toArray ( new Module [ moduleBeans . size ( ) ] ) ) ;
}
private void configureLocale ( Jackson2ObjectMapperBuilder builder ) {
Locale locale = this . jacksonProperties . getLocale ( ) ;
if ( locale ! = null ) {
builder . locale ( locale ) ;
}
}
private void configureLocale ( Jackson2ObjectMapperBuilder builder ) {
Locale locale = this . jacksonProperties . getLocale ( ) ;
if ( locale ! = null ) {
builder . locale ( locale ) ;
private static < T > Collection < T > getBeans ( ListableBeanFactory beanFactory ,
Class < T > type ) {
return BeanFactoryUtils . beansOfTypeIncludingAncestors ( beanFactory , type )
. values ( ) ;
}
}
private static < T > Collection < T > getBeans ( ListableBeanFactory beanFactory ,
Class < T > type ) {
return BeanFactoryUtils . beansOfTypeIncludingAncestors ( beanFactory , type )
. values ( ) ;
}
}