Jackson 中的@JsonMerge 註解
一、概述
在本教程中,我們將了解 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;
// Standard getters, setters and constructors
}
其次,將使用鍵盤的程序員:
class ProgrammerNotAnnotated {
String name;
String favouriteLanguage;
Keyboard keyboard;
// Standard getters, setters and constructors
}
稍後,我們將添加@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
是一個輕量級、線程安全的對象,我們可以使用它來實現與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
對象,即使新數據只包含樣式,我們也丟失了佈局屬性。合併Keyboard
等 POJO 的能力是@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. 使用@JsonMerge
合併Map
合併Map
的過程與我們已經看到的非常相似。讓我們創建一個帶有Map
對象,我們可以用它來演示:
class ObjectWithMap {
String name;
@JsonMerge
Map<String, String> stringPairs;
// Standard getters, setters and constructors
}
之後,讓我們創建一個包含地圖的起始 JSON String
,我們將使用以下內容更新我們的對象:
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
中存在所有三對。如果沒有註釋,我們只會得到來自新數據的對。
六,結論
在本文中,我們已經看到我們可以使用 Jackson 使用新傳入的 JSON 數據更新現有對象。此外,通過在我們的 Java 對像中使用@JsonMerge
註釋,我們可以讓 Jackson 合併嵌套的 POJO 和Map
。如果沒有註解,Jackson 會覆蓋它們,所以它的用處取決於我們的用例。
與往常一樣,示例的完整代碼可在 GitHub 上獲得。