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. Getting Started
3.1. Using the Default Format
我們將從演示使用
由於我們希望解釋註解的細節,因此會在請求時創建
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}
如你所見,
3.2. Using the Annotation on a Getter
現在,我們將使用
讓我們看一下為此更改更新的 User 類。我們通過註解
用於
@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"}
這裏,我們使用
上述示例演示了在字段上使用註解的方法。我們也可以在 getter 方法(屬性)中使用它。
例如,我們可能有一個在調用時計算的屬性。在這種情況下,我們可以將註解應用於 getter 方法。
請注意,我們還更改了模式以僅返回時間點的一部分:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
public Date getCurrentDate() {
return new Date();
}
並且這是輸出:
{ ... , "currentDate":"2016-12-18", ...}
3.3. Specifying the Locale
除了指定日期格式外,還可以指定序列化的區域設置。
未指定此參數將導致使用默認區域設置進行序列化:
@JsonFormat(
shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd@HH:mm:ss.SSSZ", locale = "en_GB")
public Date getCurrentDate() {
return new Date();
}
3.4. Specifying the Shape
使用
參數
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
public Date getDateNum() {
return new Date();
}
讓我們看一下輸出:
{ ..., "dateNum":1482054723876 }
4. Case-Insensitive Deserialization
Sometimes, we receive JSON documents from various sources, and they don’t follow the same letter case rule in property names. For example, one JSON document has “firstname”: “John”, but others may contain “firstName”: “John” or “FIRSTNAME”: “John”.
The default deserializer cannot automatically recognize the property names in different letter cases. Let’s understand the problem quickly through an example.
First, let’s say we have a JSON document as the input:
static final String JSON_STRING = "{\"FIRSTNAME\":\"John\",\"lastname\":\"Smith\",\"cReAtEdDaTe\":\"2016-12-18@07:53:34.740+0000\"}";
As we can see, the three properties “FIRSTNAME”, “lastname”, and “cReAtEdDaTe” follow completely different letter case rules. Now, if we deserialize this JSON document to a User object, UnrecognizedPropertyException will be raised:
assertThatThrownBy(() -> new ObjectMapper().readValue(JSON_STRING, User.class)).isInstanceOf(UnrecognizedPropertyException.class);
As the test above shows, we’ve used Assertj’s exception assertion to verify that the expected exception is thrown.
To solve this kind of problem, we must make our deserializer perform case-insensitive deserialization. So next, let’s explore how to achieve that using the @JsonFormat annotation.
The @JsonFormat annotation allows us to set a set of JsonFormat.Feature values via the with attribute: @JsonFormat(with = JsonFormat.Feature … ).
Furthermore, JsonFormat.Feature is an enum. It has predefined a set of options to specify property serialization or deserialization behaviors.
The ACCEPT_CASE_INSENSITIVE_PROPERTIES feature tells the deserializer to match property names case-insensitively. To use this feature, we can include it in a class-level @JsonFormat annotation.
Next, let’s create a UserIgnoreCase class with this annotation and feature:
@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
...
};
Now, if we deserialize our JSON input to UserIgnoreCase, it works as expected:
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));
It’s worth mentioning that we’ve used Assertj’s returns() and from() to assert multiple properties in one single assertion. It’s pretty convenient and makes the code easier to read.
5. 結論
總而言之,我們使用 @JsonFormat 來控制 Date 和 Calendar 類型的數據輸出格式。