Jackson JSON 格式化指南

Data,Jackson
Remote
0
10:54 PM · Nov 30 ,2025

1. 概述

在本教程中,我們將學習如何在 Jackson 中使用 @JsonFormat

@JsonFormat 是一個 Jackson 註解,允許我們配置屬性值的序列化或反序列化方式。例如,我們可以指定如何根據 SimpleDateFormat 格式化 DateCalendar 值。

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

我們將從演示使用 @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 字段顯示為自紀元以來(epoch)的毫秒數,這是 Date 字段的默認格式。

3.2. Using the Annotation on a 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"}

這裏,我們使用 @JsonFormat 註解格式化 createdDate 字段,使用指定的 SimpleDateFormat 格式,使用 pattern 參數。

上述示例演示了在字段上使用註解的方法。我們也可以在 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,結果是 Date 類型的默認輸出——因為它是自紀元以來(epoch)的毫秒數。

參數 pattern 在這種情況下不適用,並且被忽略:

@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 來控制 DateCalendar 類型的數據輸出格式。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.