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