Jackson 中的 @JsonFormat 指南
最后更新:2024年1月9日
1. 概述
在本教程中,我们将了解如何在 Jackson 中使用 @JsonFormat。
@JsonFormat 是一个 Jackson 注解,允许我们配置属性值的序列化或反序列化方式。例如,我们可以指定如何根据 SimpleDateFormat 格式格式化 Date 和 Calendar 值。
2. Maven 依赖
@JsonFormat 定义在 jackson-databind 包中,因此我们需要以下 Maven 依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
3. 快速入门
3.1. 使用默认格式
我们将首先演示使用 @JsonFormat 注解的概念,并使用一个代表用户的类。
由于我们想解释该注释的细节,User 对象将按需创建(而不是存储或从数据库加载),并序列化为 JSON
public class User {
private String firstName;
private String lastName;
private Date createdDate = new Date();
// standard constructor, setters and getters
}
构建并运行此代码示例将返回以下输出
{"firstName":"John","lastName":"Smith","createdDate":1482047026009}
如我们所见,createdDate 字段显示为自纪元以来的毫秒数,这是用于 Date 字段的默认格式。
3.2. 在 Getter 上使用注解
现在我们将使用 @JsonFormat 来指定序列化 createdDate 字段的格式。
让我们看一下为这次更改更新后的 User 类。我们按照所示的方式注释 createdDate 字段,以指定日期格式。
pattern 参数中使用的日期格式由 SimpleDateFormat 指定
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd@HH:mm:ss.SSSZ")
private Date createdDate;
进行此更改后,我们再次构建项目并运行它。
这是输出
{"firstName":"John","lastName":"Smith","createdDate":"2016-12-18@07:53:34.740+0000"}
我们使用指定的 SimpleDateFormat 格式使用 @JsonFormat 注解格式化了 createdDate 字段。
上面的例子演示了在字段上使用注解。我们也可以在 getter 方法(属性)中使用它。
例如,我们可能有在调用时计算的属性。在这种情况下,我们可以在 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. 指定形状
使用 @JsonFormat 且 shape 设置为 JsonFormat.Shape.NUMBER 会导致 Date 类型的默认输出——自纪元以来的毫秒数。
pattern 参数在这种情况下不适用且会被忽略
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
public Date getDateNum() {
return new Date();
}
让我们看看输出
{ ..., "dateNum":1482054723876 }
4. 不区分大小写的反序列化
有时,我们从各种来源接收 JSON 文档,它们在属性名称的大小写规则上并不相同。例如,一个 JSON 文档有 “firstname”: “John”,但其他文档可能包含 “firstName”: “John” 或 “FIRSTNAME”: “John”。
默认的反序列化器无法自动识别属性名称的大小写不同。让我们通过一个例子快速了解这个问题。
首先,假设我们有一个 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:
assertThatThrownBy(() -> new ObjectMapper().readValue(JSON_STRING, User.class)).isInstanceOf(UnrecognizedPropertyException.class);
如上面的测试所示,我们使用了 Assertj 的异常断言 来验证是否引发了预期的异常。
为了解决这个问题,我们必须使我们的反序列化器执行不区分大小写的反序列化。所以接下来,让我们探索如何使用 @JsonFormat 注解来实现这一点。
@JsonFormat 注解允许我们通过 with 属性设置一组 JsonFormat.Feature 值:@JsonFormat(with = JsonFormat.Feature … )。
此外,JsonFormat.Feature 是一个 枚举。它预定义了一组选项,用于指定属性序列化或反序列化行为。
ACCEPT_CASE_INSENSITIVE_PROPERTIES 特性告知反序列化器以不区分大小写的方式匹配属性名称。要使用此特性,我们可以将其包含在类级别的 @JsonFormat 注解中。
接下来,让我们创建一个带有此注解和特性的 UserIgnoreCase 类
@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;
// the rest is the same as the User class
...
};
现在,如果我们把我们的 JSON 输入反序列化到 UserIgnoreCase,它就能正常工作了
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 上获取。 一旦你以 Baeldung Pro 会员 身份登录,就开始学习并在项目上进行编码。















