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 會覆蓋它們,因此它的實用性取決於我們的用例。