1. 概述
本文我们将学习 Jackson 中的 @JsonFormat 注解用法。
@JsonFormat 注解允许我们配置序列化/反序列化方式,例如自定义日期格式。
2. Maven 依赖
如果是非Spring Boot项目,@JsonFormat 定义在 jackson-databind 包下,需要添加下面的依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
3. 日期格式化
3.1. 默认格式
假设我们有一个 User 类,定义如下:
public class User {
private String firstName;
private String lastName;
private Date createdDate = new Date();
// standard constructor, setters and getters
}
默认 JSON 序列化后输出如下,Date被序列化为时间戳:
{"firstName":"John","lastName":"Smith","createdDate":1482047026009}
3.2. 日期格式化
下面我们使用 @JsonFormat 指定 createdDate
日期输出格式。下面是更新后的 User 类:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd@HH:mm:ss.SSSZ")
private Date createdDate;
pattern 格式请参考 SimpleDateFormat 文档。
重新序列化,JSON变为:
{"firstName":"John","lastName":"Smith","createdDate":"2016-12-18@07:53:34.740+0000"}
我们也可以注解在 Getter 方法上:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
public Date getCurrentDate() {
return new Date();
}
输出结果如下:
{ ... , "currentDate":"2016-12-18", ...}
3.3. 指定时区
也可自定义时区:
@JsonFormat(
shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd@HH:mm:ss.SSSZ", locale = "en_GB")
public Date getCurrentDate() {
return new Date();
}
3.4. 指定 Shape
当 @JsonFormat 中 shape
设置为 JsonFormat.Shape.NUMBER
时,Date 类型会默认输出为linux时间戳(秒数),此时 pattern 参数会自动无效并忽略。
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
public Date getDateNum() {
return new Date();
}
输出结果如下所示:
{ ..., "dateNum":1482054723876 }
4. 不区分大小写
Jackson 默认反序列化时是区分大小写的,例如下面的 JSON:
static final String JSON_STRING = "{\"FIRSTNAME\":\"John\",\"lastname\":\"Smith\",\"cReAtEdDaTe\":\"2016-12-18@07:53:34.740+0000\"}";
其中 FIRSTNAME
、lastname
、cReAtEdDaTe
因为大小写问题,在解析JSON为 User 对象时,会引发 UnrecognizedPropertyException 异常。
我们使用 Assertj 的异常断言 来验证是否会抛出预期的异常:
assertThatThrownBy(() -> new ObjectMapper().readValue(JSON_STRING, User.class)).isInstanceOf(UnrecognizedPropertyException.class);
解决办法很简单,通过设置 @JsonFormat(with = JsonFormat.Feature … )
实现。
JsonFormat.Feature 是一个枚举,其中 ACCEPT_CASE_INSENSITIVE_PROPERTIES 表示大小不敏感。
@JsonFormat(with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
class UserIgnoreCase {
private String firstName;
private String lastName;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd@HH:mm:ss.SSSZ")
private Date createdDate;
// 其余部分与User类相同
...
};
然后我们再次测试就OK了:
UserIgnoreCase result = new ObjectMapper().readValue(JSON_STRING, UserIgnoreCase.class);
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSzz");
Date expectedDate = fmt.parse("2016-12-18T07:53:34.740+0000");
assertThat(result)
.isNotNull()
.returns("John", from(UserIgnoreCase::getFirstName))
.returns("Smith", from(UserIgnoreCase::getLastName))
.returns(expectedDate, from(UserIgnoreCase::getCreatedDate));
值得一提的是,我们使用了Assertj的 returns() 和 from() 实现 一次性断言多个属性。这非常方便,使代码更易于阅读。
5. 总结
总之,@JsonFormat 用于控制 Date 和 Calendar 类型的输出格式,就像上面所示的示例代码一样。
最后,文中代码可在 GitHub 中找到。