@JsonMerge 註解在 Jackson 中

Jackson
Remote
1
08:25 PM · Nov 30 ,2025

1. 概述

在本教程中,我們將探討 Jackson Java 庫中的 @JsonMerge 註解。 Jackson 以提供在 Java 應用程序中處理 JSON 的能力而聞名。 此註解允許我們在嵌套的 POJO(純 Java 對象)或 Map 中合併新的數據。 我們將研究在不使用註解時的現有功能,然後看看使用它在我們的代碼中會產生什麼不同。

2. @JsonMerge 的作用

Jackson 中最常用的特性之一是 ObjectMapper,它允許我們把 JSON 映射到 Java 對象中,以及反之。 ObjectMapper 的一項能力是讀取一個對象並使用來自 JSON String 的新數據更新它,前提是 JSON 的結構正確。 在引入 @JsonMerge 之前,這種更新能力的一個侷限性是它會覆蓋 POJO 和 Map通過此註解,嵌套 POJO 和 Map 中的屬性會在更新時合併。

讓我們看看如何在實踐中使用 @JsonMerge。 我們將創建兩個對象,首先是鍵盤:

class Keyboard {
    String style;
    String layout;
    // 標準的 getter、setter 和構造函數
}

其次,將使用鍵盤的程序員:

class ProgrammerNotAnnotated {
    String name;
    String favouriteLanguage;
    Keyboard keyboard;
    // 標準的 getter、setter 和構造函數
}

稍後,我們將添加 @JsonMerge 註解,但現在我們已經準備好了。

3. 不使用 @JsonMerge 合併

要更新一個對象,首先需要 JSON String 來表示我們想要合併的新數據:

String newData = "{\"favouriteLanguage\":\"Java\",\"keyboard\":{\"style\":\"Mechanical\"}}";

然後我們需要創建一個要用新數據更新的對象:

ProgrammerNotAnnotated programmerToUpdate = new ProgrammerNotAnnotated("John", "C++", new Keyboard("Membrane", "US"));

讓我們使用我們剛才定義的 String 和對象,看看不使用註解會發生什麼。我們首先創建一個 ObjectMapper 的實例,並使用它來創建 ObjectReader

ObjectReader objectReader = objectMapper.readerForUpdating(programmerToUpdate);

ObjectReader 是一個輕量級、線程安全的對象,我們可以用它來執行許多與 ObjectMapper 類似的功能,但具有更少的開銷。由於 ObjectReader 實例非常便宜且易於創建和配置,因此我們可以以每種序列化/反序列化為基礎來使用它們。

我們使用 ObjectReader 通過 readerForUpdating() 創建,並將要更新的對象作為唯一的參數傳遞。這是一個專門用於返回一個 ObjectReader 實例的工廠方法,該實例將使用來自 JSON String 的新數據更新給定的對象。一旦我們有了 ObjectReader,我們只需調用 readValue() 並傳入我們的新數據:

@Test
void givenAnObjectAndJson_whenNotUsingJsonMerge_thenExpectNoUpdateInPOJO() throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    ObjectReader objectReader = objectMapper.readerForUpdating(programmerToUpdate);
    ProgrammerNotAnnotated update = objectReader.readValue(newData);

    assert(update.getFavouriteLanguage()).equals("Java");
    assertNull(update.getKeyboard()
      .getLayout());
}

之後,我們可以打印出 update,以便清楚地看到最終結果:

{name='John', favouriteLanguage='Java', keyboard=Keyboard{style='Mechanical', layout='null'}}

我們可以從測試斷言和 JSON 中看到,我們的 programmerToUpdate 接收到了頂層更新,其最喜歡的語言現在是 Java。但是,我們完全覆蓋了嵌套的 Keyboard 對象,即使新數據只包含樣式,我們也丟失了佈局屬性。將 POJO(如 Keyboard)合併的能力是 @JsonMerge 註解的主要優勢,正如下一部分所見。

4. 與 @JsonMerge 合併

現在我們創建一個帶有 @JsonMerge 註解的 Programmer 對象:

class ProgrammerAnnotated {
    String name;
    String favouriteLanguage;
    @JsonMerge
    Keyboard keyboard;
    // 標準的 getter、setter 和構造函數
}

如果我們以相同的方式使用該對象,我們會得到不同的結果:

@Test
void givenAnObjectAndJson_whenUsingJsonMerge_thenExpectUpdateInPOJO() throws JsonProcessingException {
    String newData = "{\"favouriteLanguage\":\"Java\",\"keyboard\":{\"style\":\"Mechanical\",\"layout\":\"US\"}}";
    ProgrammerAnnotated programmerToUpdate = new ProgrammerAnnotated("John", "C++", new Keyboard("Membrane", "US"));

    ObjectMapper objectMapper = new ObjectMapper();
    ProgrammerAnnotated update = objectMapper.readerForUpdating(programmerToUpdate).readValue(newData);

    assert(update.getFavouriteLanguage()).equals("Java");
    // 僅在註解有效
    assert(update.getKeyboard().getLayout()).equals("US");
}

最後,我們可以打印出 update 並看到我們這次更新了嵌套的 Keyboard POJO:

{“name”:“John”, “favouriteLanguage”:“Java”, “keyboard”: {“style”:“Mechanical”, “layout”:“US”}}

註解的行為在這裏很明顯。嵌套對象中的傳入字段會覆蓋現有的字段。在新的數據中沒有匹配的字段將保持不變。

5. 合併 Map@JsonMerge

合併 Map 的過程與我們之前所見的方法非常相似。讓我們創建一個包含 Map 的對象,以便演示:

class ObjectWithMap {
    String name;
    @JsonMerge
    Map<String, String> stringPairs;
    // 標準的 getter、setter 和構造函數
}

接下來,讓我們創建一個包含我們稍後將更新對象的新 JSON String:

String newData = "{\"stringPairs\":{\"field1\":\"value1\",\"field2\":\"value2\"}}";

最後,我們需要更新的對象實例:

Map<String, String> map = new HashMap<>();
map.put("field3", "value3");
ObjectWithMap objectToUpdateWith = new ObjectWithMap("James", map);

現在我們可以使用之前使用過的相同過程來更新我們的對象:

@Test
void givenAnObjectWithAMap_whenUsingJsonMerge_thenExpectAllFieldsInMap() throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    ObjectWithMap update = objectMapper.readerForUpdating(objectToUpdateWith).readValue(newData);

    assertTrue(update.getStringPairs().containsKey("field1"));
    assertTrue(update.getStringPairs().containsKey("field2"));
    assertTrue(update.getStringPairs().containsKey("field3"));
}

如果我們再次打印出 update 以查看最終結果,它看起來像這樣:

{name='James', something={field1=value1, field3=value3, field2=value2}}

從測試和打印結果可以看出,使用該註解的結果是三個鍵值對都存在於 Map 中。如果沒有該註解,我們只會擁有來自新數據的鍵值對。

6. 結論

在本文中,我們看到可以使用 Jackson 更新現有對象,並使用新的 JSON 數據。 此外,通過在我們的 Java 對象中使用 @JsonMerge 註解,我們可以讓 Jackson 合併嵌套的 POJO 和 Map。 在沒有使用該註解的情況下,Jackson 會覆蓋它們,因此它的實用性取決於我們的用例。

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

發佈 評論

Some HTML is okay.