1 package top.infra.jackson2;
2
3 import static java.lang.Boolean.FALSE;
4 import static java.lang.Boolean.TRUE;
5
6 import com.fasterxml.jackson.core.JsonGenerator;
7 import com.fasterxml.jackson.core.JsonParser;
8 import com.fasterxml.jackson.databind.DeserializationFeature;
9 import com.fasterxml.jackson.databind.MapperFeature;
10 import com.fasterxml.jackson.databind.Module;
11 import com.fasterxml.jackson.databind.ObjectMapper;
12 import com.fasterxml.jackson.databind.SerializationFeature;
13
14 import org.springframework.beans.BeanUtils;
15 import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
16 import org.springframework.util.ClassUtils;
17
18 import java.text.DateFormat;
19 import java.text.SimpleDateFormat;
20 import java.util.List;
21 import java.util.TimeZone;
22
23
24
25
26
27
28 public class DefaultJackson2Customizer implements Jackson2Customizer {
29
30 @SuppressWarnings("unchecked")
31 @Override
32 public void customize(final Jackson2Properties properties, final ObjectMapper mapper) {
33 mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY,
34 this.feature(properties, DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, TRUE));
35
36 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
37 this.feature(properties, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, FALSE));
38
39 mapper.configure(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS,
40 this.feature(properties, JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS, FALSE));
41
42 mapper.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS,
43 this.feature(properties, JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, TRUE));
44
45 mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES,
46 this.feature(properties, JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, TRUE));
47
48
49
50
51
52
53 mapper.configure(SerializationFeature.INDENT_OUTPUT,
54 this.feature(properties, SerializationFeature.INDENT_OUTPUT, FALSE));
55
56 mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
57 this.feature(properties, SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, FALSE));
58
59
60 mapper.setTimeZone(this.timeZone(properties));
61 this.configureDateFormat(properties, mapper);
62
63
64 new Jackson2ObjectMapperBuilder().configure(mapper);
65
66 final ClassLoader moduleClassLoader = getClass().getClassLoader();
67 if (isGuavaPresent(moduleClassLoader)) {
68 try {
69 final Class<? extends Module> guavaModuleClass = (Class<? extends Module>)
70 ClassUtils.forName("com.fasterxml.jackson.datatype.guava.GuavaModule", moduleClassLoader);
71 final Module guavaModule = BeanUtils.instantiateClass(guavaModuleClass);
72 mapper.registerModule(guavaModule);
73 } catch (final ClassNotFoundException ex) {
74
75 }
76 }
77 }
78
79 @SuppressWarnings("unchecked")
80 @Override
81 public void customize(final Jackson2Properties properties, final Jackson2ObjectMapperBuilder builder) {
82 this.feature(builder, DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY,
83 this.feature(properties, DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, TRUE));
84
85 this.feature(builder, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
86 this.feature(properties, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, FALSE));
87
88 this.feature(builder, JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS,
89 this.feature(properties, JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS, FALSE));
90
91 this.feature(builder, JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS,
92 this.feature(properties, JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, TRUE));
93
94 this.feature(builder, JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES,
95 this.feature(properties, JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, TRUE));
96
97
98
99
100
101
102 this.feature(builder, SerializationFeature.INDENT_OUTPUT,
103 this.feature(properties, SerializationFeature.INDENT_OUTPUT, FALSE));
104
105 this.feature(builder, SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
106 this.feature(properties, SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, FALSE));
107
108
109 builder.timeZone(this.timeZone(properties));
110 properties.setDateFormat(this.dateFormat(properties));
111
112 final ClassLoader moduleClassLoader = this.moduleClassLoader(builder);
113 if (isGuavaPresent(moduleClassLoader)) {
114 try {
115 final Class<? extends Module> guavaModuleClass = (Class<? extends Module>)
116 ClassUtils.forName("com.fasterxml.jackson.datatype.guava.GuavaModule", moduleClassLoader);
117 final Module guavaModule = BeanUtils.instantiateClass(guavaModuleClass);
118 final List<Module> modules = this.modules(builder);
119 modules.add(guavaModule);
120 builder.modulesToInstall(modules.toArray(new Module[0]));
121 } catch (final ClassNotFoundException ex) {
122
123 }
124 }
125 }
126
127 public boolean isGuavaPresent(final ClassLoader classLoader) {
128 return ClassUtils.isPresent("com.google.common.collect.Multimap", classLoader)
129 && ClassUtils.isPresent("com.fasterxml.jackson.datatype.guava.GuavaModule", classLoader);
130 }
131
132
133
134
135
136 private void configureDateFormat(final Jackson2Properties properties, final ObjectMapper mapper) {
137
138
139 String dateFormat = properties.getDateFormat();
140 if (dateFormat != null) {
141 try {
142 Class<?> dateFormatClass = ClassUtils.forName(dateFormat, null);
143 mapper.setDateFormat((DateFormat) BeanUtils.instantiateClass(dateFormatClass));
144 } catch (ClassNotFoundException ex) {
145 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
146 dateFormat);
147
148
149
150 TimeZone timeZone = properties.getTimeZone();
151 if (timeZone == null) {
152 timeZone = new ObjectMapper().getSerializationConfig()
153 .getTimeZone();
154 }
155 simpleDateFormat.setTimeZone(timeZone);
156 mapper.setDateFormat(simpleDateFormat);
157 }
158 }
159 }
160
161 private void feature(final Jackson2ObjectMapperBuilder builder, final Object feature, final Boolean enabled) {
162 if (enabled) {
163 builder.featuresToEnable(feature);
164 } else {
165 builder.featuresToDisable(feature);
166 }
167 }
168
169 Boolean feature(final Jackson2Properties properties, final DeserializationFeature feature, final Boolean defaultValue) {
170 return properties.getDeserialization().getOrDefault(feature, defaultValue);
171 }
172
173 Boolean feature(final Jackson2Properties properties, final JsonGenerator.Feature feature, final Boolean defaultValue) {
174 return properties.getGenerator().getOrDefault(feature, defaultValue);
175 }
176
177 Boolean feature(final Jackson2Properties properties, final JsonParser.Feature feature, final Boolean defaultValue) {
178 return properties.getParser().getOrDefault(feature, defaultValue);
179 }
180
181 Boolean feature(final Jackson2Properties properties, final MapperFeature feature, final Boolean defaultValue) {
182 return properties.getMapper().getOrDefault(feature, defaultValue);
183 }
184
185 Boolean feature(final Jackson2Properties properties, final SerializationFeature feature, final Boolean defaultValue) {
186 return properties.getSerialization().getOrDefault(feature, defaultValue);
187 }
188 }