知識庫 / JSON / Jackson RSS 訂閱

@JsonMerge 註解在 Jackson 中使用

Jackson
HongKong
6
09:47 PM · Dec 05 ,2025

1. 概述

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

2. @JsonMerge 的作用

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

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

class Keyboard {
    String style;
    String layout;
    // Standard getters, setters and constructors
}

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

class ProgrammerNotAnnotated {
    String name;
    String favouriteLanguage;
    Keyboard keyboard;
    // Standard getters, setters and constructors
}

稍後,我們會添加 @JsonMerge 註解,但目前我們已經準備就緒。

3. 不使用 @JsonMerge

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

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

我們接下來需要創建我們要用新數據更新的對象:

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

讓我們使用我們剛才定義的 String 和對象,看看在沒有註解的情況下會發生什麼。首先,我們創建一個 ObjectMapper 的實例,並使用它來創建一個 ObjectReaderObjectReader 是一個輕量級、線程安全的對象,我們可以用它來實現 ObjectMapper 的很多相同功能,但具有更少的開銷。由於 ObjectReader 實例非常廉價且易於創建和配置,因此我們可以按序列化/反序列化操作使用它們。

接下來,我們使用 ObjectMapper.readerForUpdating() 創建 ObjectReader,並將要更新的對象作為唯一的參數傳遞。這是一種工廠方法,專門用於返回一個 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;
    // Standard getters, setters and constructors
}

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

@Test
void givenAnObjectAndJson_whenUsingJsonMerge_thenExpectUpdateInPOJO() throws JsonProcessingException {
    String newData = "{\"favouriteLanguage\":\"Java\",\"keyboard\":{\"style\":\"Mechanical\"}}";
    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");
    // Only works with annotation
    assert(update.getKeyboard().getLayout()).equals("US");
}

最後,我們可以打印出 update,並確認我們已經更新了本次嵌套的 Keyboard POJO:

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

標註的行為在這裏可以清楚地看到。嵌套對象中的傳入字段會覆蓋現有的字段。在新的數據中找不到匹配的字段將保持不變。

5. 合併Map 使用 @JsonMerge

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

class ObjectWithMap {
    String name;
    @JsonMerge
    Map<String, String> stringPairs;
    // Standard getters, setters and constructors
}

隨後,我們創建一個起始的 JSON 字符串,其中包含一個我們將用作更新對象的內容映射:

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

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

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 對象中使用 <em @JsonMerge</em> 註解,我們可以讓 Jackson 合併嵌套的 POJO 和 <em Map</em> 對象。 在沒有使用該註解的情況下,Jackson 會覆蓋它們,因此它的實用性取決於我們的用例。

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

發佈 評論

Some HTML is okay.