众所周知从Springboot的rest中直接返回Object对象,Spring就会调用Jackson自动转换成Json格式后返回。但有些Jackson的使用技巧你可能还不知道,或许每次都会去谷歌。这篇文章会对Jackson的一些用得少但又超级赞的功能作总结。
首先会介绍Stack Overflow 中找到的一些技巧,随后我会总结一下这次在项目中遇到的坑。
Jackson使用技巧
会分别介绍在SpringBoot的rest中的用法 ,使用注解配置的方法,还有通过ObjectMapper手动构造JSON串时的用法。
用snake_case的JSON匹配camelCase的POJO
来自 java - Jackson overcoming underscores in favor of camel-case - Stack Overflow
SpringBoot的rest中使用
两种配置方式,通过springboot的 application.properties:
1 | spring.jackson.property-naming-strategy=SNAKE_CASE |
或在类上使用注解:
1 | (PropertyNamingStrategy.SnakeCaseStrategy.class) |
配置后Controller可以取到snake_case的值并赋值给POJO。Controller返回POJO时,Jackson也会先把POJO的属性构造成snake_case的JSON再返回。
注解配置的方式使用
不推荐!
1 | class User{ |
通过ObjectMapper手动构造JSON串时使用
对ObjectMapper调用setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
。
用法示例
1 | public static class Hoge { |
不对null的POJO属性进行序列化
对属性或POJO类配置注解的方式使用
1 | (JsonInclude.Include.NON_NULL) |
在application.properties中统一配置的方式使用
如果是SpringBoot,这应该是最方便的方法了
1 | spring.jackson.default-property-inclusion=non_null #如果值为null,构造json时不加入 |
对ObjectMapper进行设置后使用
1 | public static void ignoreNullFieldByObjectMapper() throws JsonProcessingException { |
Date对象序列化后的格式
来自 Date format Mapping to JSON Jackson
注解配置的方式使用
1 | "yyyy-MM-dd HH:mm a z") (shape = JsonFormat.Shape.STRING, pattern = |
我试了下,好像必须要配时区
1 | "yyyy-MM-dd HH:mm", locale = "CHINA",timezone = "Asia/Shanghai") (shape = JsonFormat.Shape.STRING, pattern = |
在application.properties中统一配置的方式使用
1 | spring: |
对ObjectMapper进行设置后使用
1 | DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z"); |
对ObjectMapper#writeValue() 等输出的JSON进行格式化(pretty print)
对格式没需求
java - Pretty printing JSON from Jackson 2.2’s ObjectMapper - Stack Overflow
想把格式化的缩进从2格改到4格
java - Custom pretty printer using Jackson library - Stack Overflow
想把格式弄成JSON.stringify()那种
https://gist.github.com/komiya-atsushi/832a97c84ccae7cdfc2a
在项目中遇到的坑
之前不知道可以配置无视null,又觉得直接把POJO传出去太危险,毕竟会暴露表构造,所以就对很多接口都专门生成VO类。然后用BeanUtils#copyProperties在每次返回前把POJO拷贝到VO上再返回的。今天早上发现有这个功能之后马上对整个Controller层重构了一番,还是太naive啊。
还有就是对Date对象的格式化了,之前也是每次在返回前手动用DataFormat把POJO中的Date对象转成String后再拷贝个VO。像下面这样,
1 | private static <T, R> void checkDate(T po, R vo) { |
当时为了写这段也是费了好大功夫。
还有要注意的就是DTO是服务间的数据传输对象,所以感觉还是直接用Date类传递比较好。但我又是在application.properties中同一配置的date-formmat格式,所以是拿不到Date类的。比如我用SpringCloud的FeignClient试图获取带Date的DTO,果然抛了异常
1 | com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String |
因为它远程调用的另一端并没有进行相同的格式化。
这时我想到,Date默认的Json格式是2018-11-30T15:08:09.955+0800
这种。这种格式可以通过StdDateFormat.DATE_FORMAT_STR_ISO8601
获取,还有就是注解配置比application.properties配置的优先级要高,
根据这两点,我在DTO的Date类的属性上加入以下注解
1 | (pattern = StdDateFormat.DATE_FORMAT_STR_ISO8601) |
完美解决。
也就是说,当FeignClient对这个registerTime进行反序列化的时候不会尝试使用application.properties中配置的yyyy-MM-dd HH:mm
格式去解析,而是使用StdDateFormat.DATE_FORMAT_STR_ISO8601,也就是”yyyy-MM-dd’T’HH:mm:ss.SSSZ”,去解析它,而这个格式正是远程调用方进行JSON序列化时默认使用的格式,所以能反序列化成Date类。
参考文章 Jackson の痒いところ Tips