Spring Boot中Jackson ObjectMapper应用详解
Spring Boot支持与三种JSON mapping库集成:Gson、Jackson和JSON-B。Jackson是首选和默认的。
Jackson是spring-boot-starter-json的一部分,spring-boot-starter-web中包含spring-boot-starter-json。也就是说,当项目中引入spring-boot-starter-web后会自动引入spring-boot-starter-json。
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-web</artifactId>
-
</dependency>
ObjectMapper
ObjectMapper是jackson-databind包中的一个类,提供读写JSON的功能,可以方便的进行对象和JSON转换:
-
import com.fasterxml.jackson.core.JsonProcessingException;
-
import com.fasterxml.jackson.databind.ObjectMapper;
-
import java.io.IOException;
-
public final class JsonUtil {
-
private static ObjectMapper mapper = new ObjectMapper();
-
private JsonUtil() {
-
}
-
/**
-
* Serialize any Java value as a String.
-
*/
-
public static String generate(Object object) throws JsonProcessingException {
-
return mapper.writeValueAsString(object);
-
}
-
/**
-
* Deserialize JSON content from given JSON content String.
-
*/
-
public static <T> T parse(String content, Class<T> valueType) throws IOException {
-
return mapper.readValue(content, valueType);
-
}
-
}
编写一简单POJO测试类:
-
import java.util.Date;
-
public class Hero {
-
public static void main(String[] args) throws Exception {
-
System.out.println(JsonUtil.generate(new Hero("Jason", new Date())));
-
}
-
private String name;
-
private Date birthday;
-
public Hero() {
-
}
-
public Hero(String name, Date birthday) {
-
this.name = name;
-
this.birthday = birthday;
-
}
-
public String getName() {
-
return name;
-
}
-
public Date getBirthday() {
-
return birthday;
-
}
-
}
运行后输出结果如下:
[code]{"name":"Jason","birthday":1540909420353}
可以看到日期转换为长整型。
ObjectMapper默认序列化配置启用了SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,日期将转换为Timestamp。可查看如下源码:
-
public ObjectMapper(JsonFactory jf, DefaultSerializerProvider sp, DefaultDeserializationContext dc) {
-
...
-
BaseSettings base = DEFAULT_BASE.withClassIntrospector(defaultClassIntrospector());
-
_configOverrides = new ConfigOverrides();
-
_serializationConfig = new SerializationConfig(base, _subtypeResolver, mixins, rootNames, _configOverrides);
-
...
-
}
-
public SerializationConfig(BaseSettings base, SubtypeResolver str, SimpleMixInResolver mixins, RootNameLookup rootNames,
-
ConfigOverrides configOverrides)
-
{
-
super(base, str, mixins, rootNames, configOverrides);
-
_serFeatures = collectFeatureDefaults(SerializationFeature.class);
-
_filterProvider = null;
-
_defaultPrettyPrinter = DEFAULT_PRETTY_PRINTER;
-
_generatorFeatures = 0;
-
_generatorFeaturesToChange = 0;
-
_formatWriteFeatures = 0;
-
_formatWriteFeaturesToChange = 0;
-
}
默认情况下,Date类型序列化将调用DateSerializer的_timestamp 方法:
-
/**
-
* For efficiency, we will serialize Dates as longs, instead of
-
* potentially more readable Strings.
-
*/
-
@JacksonStdImpl
-
@SuppressWarnings("serial")
-
public class DateSerializer extends DateTimeSerializerBase<Date> {
-
...
-
@Override
-
protected long _timestamp(Date value) {
-
return (value == null) ? 0L : value.getTime();
-
}
-
@Override
-
public void serialize(Date value, JsonGenerator g, SerializerProvider provider) throws IOException {
-
if (_asTimestamp(provider)) {
-
g.writeNumber(_timestamp(value));
-
return;
-
}
-
_serializeAsString(value, g, provider);
-
}
-
}
DateTimeSerializerBase的_asTimestamp方法:
-
protected boolean _asTimestamp(SerializerProvider serializers)
-
{
-
if (_useTimestamp != null) {
-
return _useTimestamp.booleanValue();
-
}
-
if (_customFormat == null) {
-
if (serializers != null) {
-
return serializers.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
-
}
-
// 12-Jun-2014, tatu: Is it legal not to have provider? Was NPE:ing earlier so leave a check
-
throw new IllegalArgumentException("Null SerializerProvider passed for "+handledType().getName());
-
}
-
return false;
-
}
禁用WRITE_DATES_AS_TIMESTAMPS
若要将日期序列化为字符串,可禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS:
[code]mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
这时序列化将调用StdDateFormat的format()方法,使用ISO-8601兼容格式"yyyy-MM-dd'T'HH:mm:ss.SSSZ",输出内容如下:
4000[code]{"name":"Jason","birthday":"2018-10-31T03:07:34.485+0000"}
StdDateFormat反序列化支持ISO-8601兼容格式和RFC-1123("EEE, dd MMM yyyy HH:mm:ss zzz")格式。
@JsonFormat
使用@JsonFormat注解,代替全局设置,是一种更灵活的方法:
-
@JsonFormat(shape = JsonFormat.Shape.STRING)
-
private Date birthday;
还可以定义pattern:
-
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
-
private Date birthday;
当自定义pattern后,将创建新的SimpleDateFormat实例来序列化日期,参见DateTimeSerializerBase的createContextual()方法:
-
public JsonSerializer<?> createContextual(SerializerProvider serializers, BeanProperty property) throws JsonMappingException
-
{
-
...
-
if (format.hasPattern()) {
-
final Locale loc = format.hasLocale() ? format.getLocale() : serializers.getLocale();
-
SimpleDateFormat df = new SimpleDateFormat(format.getPattern(), loc);
-
TimeZone tz = format.hasTimeZone() ? format.getTimeZone() : serializers.getTimeZone();
-
df.setTimeZone(tz);
-
return withFormat(Boolean.FALSE, df);
-
}
-
...
-
}
Spring Boot与Jackson ObjectMapper
Spring Boot使用HttpMessageConverters处理HTTP交换中的内容转换。当classpath中存在Jackson时,Jackson2ObjectMapperBuilder将是默认的Converter,源码请查看HttpMessageConverters和WebMvcConfigurationSupport:
HttpMessageConverters
-
private List<HttpMessageConverter<?>> getDefaultConverters() {
-
List<HttpMessageConverter<?>> converters = new ArrayList<>();
-
if (ClassUtils.isPresent("org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport", null)) {
-
converters.addAll(new WebMvcConfigurationSupport() {
-
public List<HttpMessageConverter<?>> defaultMessageConverters() {
-
return super.getMessageConverters();
-
}
-
}.defaultMessageConverters());
-
}
-
else {
-
converters.addAll(new RestTemplate().getMessageConverters());
-
}
-
reorderXmlConvertersToEnd(converters);
-
return converters;
-
}
WebMvcConfigurationSupport
-
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
-
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
-
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
-
messageConverters.add(new ByteArrayHttpMessageConverter());
-
messageConverters.add(stringHttpMessageConverter);
-
messageConverters.add(new ResourceHttpMessageConverter());
-
messageConverters.add(new ResourceRegionHttpMessageConverter());
-
messageConverters.add(new SourceHttpMessageConverter<>());
-
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
-
...
-
if (jackson2Present) {
-
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
-
if (this.applicationContext != null) {
-
builder.applicationContext(this.applicationContext);
-
}
-
messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
-
}
-
...
-
}
默认,Jackson2ObjectMapperBuilder将创建ObjectMapper实例:
Jackson2ObjectMapperBuilder
-
public <T extends ObjectMapper> T build() {
-
ObjectMapper mapper;
-
if (this.createXmlMapper) {
-
mapper = (this.defaultUseWrapper != null ?
-
new XmlObjectMapperInitializer().create(this.defaultUseWrapper) :
-
new XmlObjectMapperInitializer().create());
-
}
-
else {
-
mapper = (this.factory != null ? new ObjectMapper(this.factory) : new ObjectMapper());
-
}
-
configure(mapper);
-
return (T) mapper;
-
}
ObjectMapper以下属性被禁用:
- MapperFeature.DEFAULT_VIEW_INCLUSION
- DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
- SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
Jackson2ObjectMapperBuilder
-
private void customizeDefaultFeatures(ObjectMapper objectMapper) {
-
if (!this.features.containsKey(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
-
configureFeature(objectMapper, MapperFeature.DEFAULT_VIEW_INCLUSION, false);
-
}
-
if (!this.features.containsKey(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
-
configureFeature(objectMapper, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
-
}
-
}
JacksonAutoConfiguration
-
static {
-
Map<Object, Boolean> featureDefaults = new HashMap<>();
-
featureDefaults.put(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
-
FEATURE_DEFAULTS = Collections.unmodifiableMap(featureDefaults);
-
}
自定义Jackson ObjectMapper配置
针对ObjectMapper的六种Feature,Spring Boot都提供了相应的配置,列表如下:
Feature(Enum) | Spring Boot Property | Values |
---|---|---|
com.fasterxml.jackson.databind.DeserializationFeature | spring.jackson.deserialization.feature_name | true, false |
com.fasterxml.jackson.core.JsonGenerator.Feature | spring.jackson.generator.feature_name | true, false |
com.fasterxml.jackson.databind.MapperFeature | spring.jackson.mapper.feature_name | true, false |
com.fasterxml.jackson.core.JsonParser.Feature | spring.jackson.parser.feature_name | true, false |
com.fasterxml.jackson.databind.SerializationFeature | spring.jackson.serialization.feature_name | true, false |
com.fasterxml.jackson.annotation.JsonInclude.Include | spring.jackson.default-property-inclusion | always, non_null, non_absent, non_default, non_empty |
例如,为启用美化打印,设置spring.jackson.serialization.indent_output=true,相当于启用SerializationFeature.INDENT_OUTPUT,配置中忽略feature_name大小写。
其他的Jackson配置属性:
- spring.jackson.date-format= # Date format string or a fully-qualified date format class name. For instance,
yyyy-MM-dd HH:mm:ss
. - spring.jackson.joda-date-time-format= # Joda date time format string. If not configured, "date-format" is used as a fallback if it is configured with a format string.
- spring.jackson.locale= # Locale used for formatting.
- spring.jackson.property-naming-strategy= # One of the constants on Jackson's PropertyNamingStrategy. Can also be a fully-qualified class name of a PropertyNamingStrategy subclass.
- spring.jackson.time-zone= # Time zone used when formatting dates. For instance, "America/Los_Angeles" or "GMT+10".
其中spring.jackson.date-format默认值为com.fasterxml.jackson.databind.util.StdDateFormat。
@DateTimeFormat和@JsonFormat
在REST编程中,当提交application/json的POST/PUT请求时,JSON会通过Jackson进行转换。当提交GET请求时,如参数中包含日期,后台代码需要使用注解@DateTimeFormat:
-
@DateTimeFormat(pattern = "yyyy-MM-dd")
-
private Date startDate;
两者可以同时使用:
-
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
-
@DateTimeFormat(pattern = "yyyy-MM-dd")
-
private Date startDate;
-
参考:
- 〔转〕Word域的应用和详解4_日期和时间
- Android应用坐标系统全面详解
- Android Orientation Sensor(方向传感器)详解与应用
- Shell脚本中倒计时 4000 应用 – echo用法详解
- Vue组件知识详解及应用
- Android-Universal-Image-Loader应用详解
- hadoop应用开发技术详解
- EXCEL函数LookUp, VLOOKUP,HLOOKUP应用详解(含中文参数解释)
- 详解WEB应用的部署文件web.xml
- 图的应用详解-数据结构
- 一个应用实例详解卡尔曼滤波及其算法实现(通俗易懂哦)
- iOS应用程序生命周期(前后台切换,应用的各种状态)详解
- <ZZ>Linux rpm 命令参数使用详解[介绍和应用]
- Hadoop之Hive架构详解及应用
- 线段树详解 (原理,实现与应用)
- Memcache升级版之CouchBase [三]JAVA应用详解
- Linux RPM 命令参数使用详解[介绍和应用]
- Meanshift-视频跟踪技术上的应用详解
- LSTM入门必读:从入门基础到工作方式详解 By 机器之心2017年7月24日 12:57 长短期记忆(LSTM)是一种非常重要的神经网络技术,其在语音识别和自然语言处理等许多领域都得到了广泛的应用
- 六-Statistics统计工具功能详解与应用