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 * &lt;bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
065 *   &lt;property name="objectMapper">
066 *     &lt;bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
067 *       p:autoDetectFields="false"
068 *       p:autoDetectGettersSetters="false"
069 *       p:annotationIntrospector-ref="jaxbAnnotationIntrospector" />
070 *   &lt;/property>
071 * &lt;/bean>
072 * </pre>
073 *
074 * <p>Example usage with MappingJackson2JsonView:
075 *
076 * <pre class="code">
077 * &lt;bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
078 *   &lt;property name="objectMapper">
079 *     &lt;bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
080 *       p:failOnEmptyBeans="false"
081 *       p:indentOutput="true">
082 *       &lt;property name="serializers">
083 *         &lt;array>
084 *           &lt;bean class="org.mycompany.MyCustomSerializer" />
085 *         &lt;/array>
086 *       &lt;/property>
087 *     &lt;/bean>
088 *   &lt;/property>
089 * &lt;/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 * &lt;bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
098 *   &lt;property name="featuresToEnable">
099 *     &lt;array>
100 *       &lt;util:constant static-field="com.fasterxml.jackson.databind.SerializationFeature.WRAP_ROOT_VALUE"/>
101 *       &lt;util:constant static-field="com.fasterxml.jackson.databind.SerializationFeature.CLOSE_CLOSEABLE"/>
102 *     &lt;/array>
103 *   &lt;/property>
104 *   &lt;property name="featuresToDisable">
105 *     &lt;array>
106 *       &lt;util:constant static-field="com.fasterxml.jackson.databind.MapperFeature.USE_ANNOTATIONS"/>
107 *     &lt;/array>
108 *   &lt;/property>
109 * &lt;/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 * &lt;bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
127 *   &lt;property name="modulesToInstall" value="myapp.jackson.MySampleModule,myapp.jackson.MyOtherModule"/>
128 * &lt;/bean
129 * </pre>
130 *
131 * <p>Compatible with Jackson 2.6 and higher, as of Spring 4.3.
132 *
133 * @author <a href="mailto:[email protected]">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}