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\"}";

其中 FIRSTNAMElastnamecReAtEdDaTe 因为大小写问题,在解析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 中找到。


» 下一篇: Vavr介绍