001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.http.converter.json; 018 019import java.text.DateFormat; 020import java.text.SimpleDateFormat; 021import java.util.List; 022import java.util.Locale; 023import java.util.Map; 024import java.util.TimeZone; 025 026import com.fasterxml.jackson.annotation.JsonFilter; 027import com.fasterxml.jackson.annotation.JsonInclude; 028import com.fasterxml.jackson.databind.AnnotationIntrospector; 029import com.fasterxml.jackson.databind.DeserializationFeature; 030import com.fasterxml.jackson.databind.JsonDeserializer; 031import com.fasterxml.jackson.databind.JsonSerializer; 032import com.fasterxml.jackson.databind.KeyDeserializer; 033import com.fasterxml.jackson.databind.MapperFeature; 034import com.fasterxml.jackson.databind.Module; 035import com.fasterxml.jackson.databind.ObjectMapper; 036import com.fasterxml.jackson.databind.PropertyNamingStrategy; 037import com.fasterxml.jackson.databind.SerializationFeature; 038import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; 039import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; 040import com.fasterxml.jackson.databind.ser.FilterProvider; 041import com.fasterxml.jackson.dataformat.xml.XmlMapper; 042 043import org.springframework.beans.factory.BeanClassLoaderAware; 044import org.springframework.beans.factory.FactoryBean; 045import org.springframework.beans.factory.InitializingBean; 046import org.springframework.context.ApplicationContext; 047import org.springframework.context.ApplicationContextAware; 048 049/** 050 * A {@link FactoryBean} for creating a Jackson 2.x {@link ObjectMapper} (default) or 051 * {@link XmlMapper} ({@code createXmlMapper} property set to true) with setters 052 * to enable or disable Jackson features from within XML configuration. 053 * 054 * <p>It customizes Jackson defaults properties with the following ones: 055 * <ul> 056 * <li>{@link MapperFeature#DEFAULT_VIEW_INCLUSION} is disabled</li> 057 * <li>{@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} is disabled</li> 058 * </ul> 059 * 060 * <p>Example usage with 061 * {@link MappingJackson2HttpMessageConverter}: 062 * 063 * <pre class="code"> 064 * <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> 065 * <property name="objectMapper"> 066 * <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean" 067 * p:autoDetectFields="false" 068 * p:autoDetectGettersSetters="false" 069 * p:annotationIntrospector-ref="jaxbAnnotationIntrospector" /> 070 * </property> 071 * </bean> 072 * </pre> 073 * 074 * <p>Example usage with MappingJackson2JsonView: 075 * 076 * <pre class="code"> 077 * <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"> 078 * <property name="objectMapper"> 079 * <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean" 080 * p:failOnEmptyBeans="false" 081 * p:indentOutput="true"> 082 * <property name="serializers"> 083 * <array> 084 * <bean class="org.mycompany.MyCustomSerializer" /> 085 * </array> 086 * </property> 087 * </bean> 088 * </property> 089 * </bean> 090 * </pre> 091 * 092 * <p>In case there are no specific setters provided (for some rarely used options), 093 * you can still use the more general methods {@link #setFeaturesToEnable} and 094 * {@link #setFeaturesToDisable}. 095 * 096 * <pre class="code"> 097 * <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> 098 * <property name="featuresToEnable"> 099 * <array> 100 * <util:constant static-field="com.fasterxml.jackson.databind.SerializationFeature.WRAP_ROOT_VALUE"/> 101 * <util:constant static-field="com.fasterxml.jackson.databind.SerializationFeature.CLOSE_CLOSEABLE"/> 102 * </array> 103 * </property> 104 * <property name="featuresToDisable"> 105 * <array> 106 * <util:constant static-field="com.fasterxml.jackson.databind.MapperFeature.USE_ANNOTATIONS"/> 107 * </array> 108 * </property> 109 * </bean> 110 * </pre> 111 * 112 * <p>It also automatically registers the following well-known modules if they are 113 * detected on the classpath: 114 * <ul> 115 * <li><a href="https://github.com/FasterXML/jackson-datatype-jdk7">jackson-datatype-jdk7</a>: support for Java 7 types like {@link java.nio.file.Path}</li> 116 * <li><a href="https://github.com/FasterXML/jackson-datatype-jdk8">jackson-datatype-jdk8</a>: support for other Java 8 types like {@link java.util.Optional}</li> 117 * <li><a href="https://github.com/FasterXML/jackson-datatype-jsr310">jackson-datatype-jsr310</a>: support for Java 8 Date & Time API types</li> 118 * <li><a href="https://github.com/FasterXML/jackson-datatype-joda">jackson-datatype-joda</a>: support for Joda-Time types</li> 119 * <li><a href="https://github.com/FasterXML/jackson-module-kotlin">jackson-module-kotlin</a>: support for Kotlin classes and data classes</li> 120 * </ul> 121 * 122 * <p>In case you want to configure Jackson's {@link ObjectMapper} with a custom {@link Module}, 123 * you can register one or more such Modules by class name via {@link #setModulesToInstall}: 124 * 125 * <pre class="code"> 126 * <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> 127 * <property name="modulesToInstall" value="myapp.jackson.MySampleModule,myapp.jackson.MyOtherModule"/> 128 * </bean 129 * </pre> 130 * 131 * <p>Compatible with Jackson 2.6 and higher, as of Spring 4.3. 132 * 133 * @author <a href="mailto:dmitry.katsubo@gmail.com">Dmitry Katsubo</a> 134 * @author Rossen Stoyanchev 135 * @author Brian Clozel 136 * @author Juergen Hoeller 137 * @author Tadaya Tsuyukubo 138 * @author Sebastien Deleuze 139 * @since 3.2 140 */ 141public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper>, BeanClassLoaderAware, 142 ApplicationContextAware, InitializingBean { 143 144 private final Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); 145 146 private ObjectMapper objectMapper; 147 148 149 /** 150 * Set the {@link ObjectMapper} instance to use. If not set, the {@link ObjectMapper} 151 * will be created using its default constructor. 152 */ 153 public void setObjectMapper(ObjectMapper objectMapper) { 154 this.objectMapper = objectMapper; 155 } 156 157 /** 158 * If set to true and no custom {@link ObjectMapper} has been set, a {@link XmlMapper} 159 * will be created using its default constructor. 160 * @since 4.1 161 */ 162 public void setCreateXmlMapper(boolean createXmlMapper) { 163 this.builder.createXmlMapper(createXmlMapper); 164 } 165 166 /** 167 * Define the format for date/time with the given {@link DateFormat}. 168 * <p>Note: Setting this property makes the exposed {@link ObjectMapper} 169 * non-thread-safe, according to Jackson's thread safety rules. 170 * @see #setSimpleDateFormat(String) 171 */ 172 public void setDateFormat(DateFormat dateFormat) { 173 this.builder.dateFormat(dateFormat); 174 } 175 176 /** 177 * Define the date/time format with a {@link SimpleDateFormat}. 178 * <p>Note: Setting this property makes the exposed {@link ObjectMapper} 179 * non-thread-safe, according to Jackson's thread safety rules. 180 * @see #setDateFormat(DateFormat) 181 */ 182 public void setSimpleDateFormat(String format) { 183 this.builder.simpleDateFormat(format); 184 } 185 186 /** 187 * Override the default {@link Locale} to use for formatting. 188 * Default value used is {@link Locale#getDefault()}. 189 * @since 4.1.5 190 */ 191 public void setLocale(Locale locale) { 192 this.builder.locale(locale); 193 } 194 195 /** 196 * Override the default {@link TimeZone} to use for formatting. 197 * Default value used is UTC (NOT local timezone). 198 * @since 4.1.5 199 */ 200 public void setTimeZone(TimeZone timeZone) { 201 this.builder.timeZone(timeZone); 202 } 203 204 /** 205 * Set an {@link AnnotationIntrospector} for both serialization and deserialization. 206 */ 207 public void setAnnotationIntrospector(AnnotationIntrospector annotationIntrospector) { 208 this.builder.annotationIntrospector(annotationIntrospector); 209 } 210 211 /** 212 * Specify a {@link com.fasterxml.jackson.databind.PropertyNamingStrategy} to 213 * configure the {@link ObjectMapper} with. 214 * @since 4.0.2 215 */ 216 public void setPropertyNamingStrategy(PropertyNamingStrategy propertyNamingStrategy) { 217 this.builder.propertyNamingStrategy(propertyNamingStrategy); 218 } 219 220 /** 221 * Specify a {@link TypeResolverBuilder} to use for Jackson's default typing. 222 * @since 4.2.2 223 */ 224 public void setDefaultTyping(TypeResolverBuilder<?> typeResolverBuilder) { 225 this.builder.defaultTyping(typeResolverBuilder); 226 } 227 228 /** 229 * Set a custom inclusion strategy for serialization. 230 * @see com.fasterxml.jackson.annotation.JsonInclude.Include 231 */ 232 public void setSerializationInclusion(JsonInclude.Include serializationInclusion) { 233 this.builder.serializationInclusion(serializationInclusion); 234 } 235 236 /** 237 * Set the global filters to use in order to support {@link JsonFilter @JsonFilter} annotated POJO. 238 * @since 4.2 239 * @see Jackson2ObjectMapperBuilder#filters(FilterProvider) 240 */ 241 public void setFilters(FilterProvider filters) { 242 this.builder.filters(filters); 243 } 244 245 /** 246 * Add mix-in annotations to use for augmenting specified class or interface. 247 * @param mixIns Map of entries with target classes (or interface) whose annotations 248 * to effectively override as key and mix-in classes (or interface) whose 249 * annotations are to be "added" to target's annotations as value. 250 * @since 4.1.2 251 * @see com.fasterxml.jackson.databind.ObjectMapper#addMixInAnnotations(Class, Class) 252 */ 253 public void setMixIns(Map<Class<?>, Class<?>> mixIns) { 254 this.builder.mixIns(mixIns); 255 } 256 257 /** 258 * Configure custom serializers. Each serializer is registered for the type 259 * returned by {@link JsonSerializer#handledType()}, which must not be {@code null}. 260 * @see #setSerializersByType(Map) 261 */ 262 public void setSerializers(JsonSerializer<?>... serializers) { 263 this.builder.serializers(serializers); 264 } 265 266 /** 267 * Configure custom serializers for the given types. 268 * @see #setSerializers(JsonSerializer...) 269 */ 270 public void setSerializersByType(Map<Class<?>, JsonSerializer<?>> serializers) { 271 this.builder.serializersByType(serializers); 272 } 273 274 /** 275 * Configure custom deserializers. Each deserializer is registered for the type 276 * returned by {@link JsonDeserializer#handledType()}, which must not be {@code null}. 277 * @since 4.3 278 * @see #setDeserializersByType(Map) 279 */ 280 public void setDeserializers(JsonDeserializer<?>... deserializers) { 281 this.builder.deserializers(deserializers); 282 } 283 284 /** 285 * Configure custom deserializers for the given types. 286 */ 287 public void setDeserializersByType(Map<Class<?>, JsonDeserializer<?>> deserializers) { 288 this.builder.deserializersByType(deserializers); 289 } 290 291 /** 292 * Shortcut for {@link MapperFeature#AUTO_DETECT_FIELDS} option. 293 */ 294 public void setAutoDetectFields(boolean autoDetectFields) { 295 this.builder.autoDetectFields(autoDetectFields); 296 } 297 298 /** 299 * Shortcut for {@link MapperFeature#AUTO_DETECT_SETTERS}/ 300 * {@link MapperFeature#AUTO_DETECT_GETTERS}/{@link MapperFeature#AUTO_DETECT_IS_GETTERS} 301 * options. 302 */ 303 public void setAutoDetectGettersSetters(boolean autoDetectGettersSetters) { 304 this.builder.autoDetectGettersSetters(autoDetectGettersSetters); 305 } 306 307 /** 308 * Shortcut for {@link MapperFeature#DEFAULT_VIEW_INCLUSION} option. 309 * @since 4.1 310 */ 311 public void setDefaultViewInclusion(boolean defaultViewInclusion) { 312 this.builder.defaultViewInclusion(defaultViewInclusion); 313 } 314 315 /** 316 * Shortcut for {@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} option. 317 * @since 4.1.1 318 */ 319 public void setFailOnUnknownProperties(boolean failOnUnknownProperties) { 320 this.builder.failOnUnknownProperties(failOnUnknownProperties); 321 } 322 323 /** 324 * Shortcut for {@link SerializationFeature#FAIL_ON_EMPTY_BEANS} option. 325 */ 326 public void setFailOnEmptyBeans(boolean failOnEmptyBeans) { 327 this.builder.failOnEmptyBeans(failOnEmptyBeans); 328 } 329 330 /** 331 * Shortcut for {@link SerializationFeature#INDENT_OUTPUT} option. 332 */ 333 public void setIndentOutput(boolean indentOutput) { 334 this.builder.indentOutput(indentOutput); 335 } 336 337 /** 338 * Define if a wrapper will be used for indexed (List, array) properties or not by 339 * default (only applies to {@link XmlMapper}). 340 * @since 4.3 341 */ 342 public void setDefaultUseWrapper(boolean defaultUseWrapper) { 343 this.builder.defaultUseWrapper(defaultUseWrapper); 344 } 345 346 /** 347 * Specify features to enable. 348 * @see com.fasterxml.jackson.core.JsonParser.Feature 349 * @see com.fasterxml.jackson.core.JsonGenerator.Feature 350 * @see com.fasterxml.jackson.databind.SerializationFeature 351 * @see com.fasterxml.jackson.databind.DeserializationFeature 352 * @see com.fasterxml.jackson.databind.MapperFeature 353 */ 354 public void setFeaturesToEnable(Object... featuresToEnable) { 355 this.builder.featuresToEnable(featuresToEnable); 356 } 357 358 /** 359 * Specify features to disable. 360 * @see com.fasterxml.jackson.core.JsonParser.Feature 361 * @see com.fasterxml.jackson.core.JsonGenerator.Feature 362 * @see com.fasterxml.jackson.databind.SerializationFeature 363 * @see com.fasterxml.jackson.databind.DeserializationFeature 364 * @see com.fasterxml.jackson.databind.MapperFeature 365 */ 366 public void setFeaturesToDisable(Object... featuresToDisable) { 367 this.builder.featuresToDisable(featuresToDisable); 368 } 369 370 /** 371 * Set a complete list of modules to be registered with the {@link ObjectMapper}. 372 * <p>Note: If this is set, no finding of modules is going to happen - not by 373 * Jackson, and not by Spring either (see {@link #setFindModulesViaServiceLoader}). 374 * As a consequence, specifying an empty list here will suppress any kind of 375 * module detection. 376 * <p>Specify either this or {@link #setModulesToInstall}, not both. 377 * @since 4.0 378 * @see com.fasterxml.jackson.databind.Module 379 */ 380 public void setModules(List<Module> modules) { 381 this.builder.modules(modules); 382 } 383 384 /** 385 * Specify one or more modules by class (or class name in XML) 386 * to be registered with the {@link ObjectMapper}. 387 * <p>Modules specified here will be registered after 388 * Spring's autodetection of JSR-310 and Joda-Time, or Jackson's 389 * finding of modules (see {@link #setFindModulesViaServiceLoader}), 390 * allowing to eventually override their configuration. 391 * <p>Specify either this or {@link #setModules}, not both. 392 * @since 4.0.1 393 * @see com.fasterxml.jackson.databind.Module 394 */ 395 @SuppressWarnings("unchecked") 396 public void setModulesToInstall(Class<? extends Module>... modules) { 397 this.builder.modulesToInstall(modules); 398 } 399 400 /** 401 * Set whether to let Jackson find available modules via the JDK ServiceLoader, 402 * based on META-INF metadata in the classpath. Requires Jackson 2.2 or higher. 403 * <p>If this mode is not set, Spring's Jackson2ObjectMapperFactoryBean itself 404 * will try to find the JSR-310 and Joda-Time support modules on the classpath - 405 * provided that Java 8 and Joda-Time themselves are available, respectively. 406 * @since 4.0.1 407 * @see com.fasterxml.jackson.databind.ObjectMapper#findModules() 408 */ 409 public void setFindModulesViaServiceLoader(boolean findModules) { 410 this.builder.findModulesViaServiceLoader(findModules); 411 } 412 413 @Override 414 public void setBeanClassLoader(ClassLoader beanClassLoader) { 415 this.builder.moduleClassLoader(beanClassLoader); 416 } 417 418 /** 419 * Customize the construction of Jackson handlers 420 * ({@link JsonSerializer}, {@link JsonDeserializer}, {@link KeyDeserializer}, 421 * {@code TypeResolverBuilder} and {@code TypeIdResolver}). 422 * @since 4.1.3 423 * @see Jackson2ObjectMapperFactoryBean#setApplicationContext(ApplicationContext) 424 */ 425 public void setHandlerInstantiator(HandlerInstantiator handlerInstantiator) { 426 this.builder.handlerInstantiator(handlerInstantiator); 427 } 428 429 /** 430 * Set the builder {@link ApplicationContext} in order to autowire Jackson handlers 431 * ({@link JsonSerializer}, {@link JsonDeserializer}, {@link KeyDeserializer}, 432 * {@code TypeResolverBuilder} and {@code TypeIdResolver}). 433 * @since 4.1.3 434 * @see Jackson2ObjectMapperBuilder#applicationContext(ApplicationContext) 435 * @see SpringHandlerInstantiator 436 */ 437 @Override 438 public void setApplicationContext(ApplicationContext applicationContext) { 439 this.builder.applicationContext(applicationContext); 440 } 441 442 443 @Override 444 public void afterPropertiesSet() { 445 if (this.objectMapper != null) { 446 this.builder.configure(this.objectMapper); 447 } 448 else { 449 this.objectMapper = this.builder.build(); 450 } 451 } 452 453 /** 454 * Return the singleton ObjectMapper. 455 */ 456 @Override 457 public ObjectMapper getObject() { 458 return this.objectMapper; 459 } 460 461 @Override 462 public Class<?> getObjectType() { 463 return (this.objectMapper != null ? this.objectMapper.getClass() : null); 464 } 465 466 @Override 467 public boolean isSingleton() { 468 return true; 469 } 470 471}