1. 概述
在本教程中,我們將探索如何將 Jackson 的原始數據類型 JsonNode 轉換為類型為 Java 集合的不同方法。雖然我們可以使用 JsonNode 本身讀取 JSON 數據,但將其轉換為 Java 集合可能會有所幫助。Java 集合比原始 JSON 數據具有優勢,例如類型安全、更快的處理速度以及更多類型特定的操作。
2. 示例設置
在我們的代碼示例中,我們將探討如何將 JsonNode 轉換為 List 或 Map 對象列表。讓我們設置示例的構建塊。
2.1 依賴
首先,讓我們將 Jackson Core 依賴添加到 pom.xml 文件中:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.17.0</version>
</dependency>
2.2 JSON 數據
接下來,讓我們定義用於我們用例的 JSON:
{
"persons": [
{
"name": "John",
"age": 30
},
{
"name": "Alice",
"age": 25
}
],
"idToPerson" : {
"1234": {
"name": "John",
"age": 30
},
"1235": {
"name": "Alice",
"age": 25
}
}
}
在上面的 JSON 中,我們有一個 JSON 數組 persons 和一個 JSON 對象 idToPerson。我們將探討如何將它們轉換為 Java 集合。
2.3 DTO
讓我們定義一個 Person 類,可用於我們的示例:
public class Person {
private String name;
private int age;
// constructors/getters/setters
}
2.4 將 JSON 字符串轉換為 JsonNode
如果我們想從整個 JSON 對象中讀取對象,我們可以使用 Jackson 中的 ObjectMapper 類來執行此操作:
JsonNode rootNode = new ObjectMapper().readTree(jsonString);
JsonNode childNode = rootNode.get("persons");
要將整個 JSON 轉換為 JsonNode 對象,我們使用 readTree() 方法。然後,我們使用 get() 方法遍歷 JsonNode 對象,該方法返回具有指定名稱的嵌套對象。
3. 手動轉換
在檢查庫方法之前,讓我們看看如何將 JsonNode 轉換為一個集合的手動方法。
3.1. 手動將 JsonNode 轉換為 List
要將 JsonNode 轉換為列表,我們可以逐個遍歷它並創建一個 List 對象:
List<Person> manualJsonNodeToList(JsonNode personsNode) {
List<Person> people = new ArrayList<>();
for (JsonNode node : personsNode) {
Person person = new Person(node.get("name").asText(), node.get("age").asInt());
people.add(person);
}
return people;
}
在這裏,我們使用循環來遍歷輸入節點的所有子節點。這僅在我們的輸入節點是數組的情況下才可能。
對於每個節點,我們創建一個 Person 對象並將它添加到列表中。我們使用 get(fieldName) 方法從節點中獲取 name 和 age。JsonNode 提供了各種方法來將返回的值轉換為原始 Java 類型。在這裏,asText() 和 asInt() 方法將值轉換為 String 和 int, respectively。
3.2. 手動將 JsonNode 轉換為 Map
讓我們看看類似的轉換對於 map 的情況:
Map<String, Person> manualJsonNodeToMap(JsonNode idToPersonsNode) {
Map<String, Person> mapOfIdToPerson = new HashMap<>();
idToPersonsNode.fields()
.forEachRemaining(node -> mapOfIdToPerson.put(node.getKey(),
new Person(node.getValue().get("name").asText(), node.getValue().get("age").asInt())));
return mapOfIdToPerson;
}
在這裏,我們使用 fields() 方法來迭代 map 鍵值對。它返回一個 Iterator<Map.Entry<String, JsonNode>> 對象,我們可以進一步處理它。接下來,我們讀取每個條目並將其放入我們的 Map 中。
4. 使用 Jackson 的 readValue() 和 convertValue()
Jackson 提供了多種將 JsonNode 轉換為 Java 對象的方法。下面我們來看其中兩種。
4.1. 使用 readValue()
readValue() 方法可用於使用 TypeReference 將其轉換為 List 或 Map:
List<Person> readValueJsonNodeToList(JsonNode personsNode) throws IOException {
TypeReference<List<Person>> typeReferenceList = new TypeReference<List<Person>>() {};
return new ObjectMapper().readValue(personsNode.traverse(), typeReferenceList);
}
Map<String, Person> readValueJsonNodeToMap(JsonNode idToPersonsNode) throws IOException {
TypeReference<Map<String, Person>> typeReferenceMap = new TypeReference<Map<String, Person>>() {};
return new ObjectMapper().readValue(idToPersonsNode.traverse(), typeReferenceMap);
}
首先,我們創建一個 TypeReference 對象,通過傳遞所需的轉換目標類型來實現。然後,我們調用 readValue() 方法,其中 JsonParser 由 jsonNode.traverse() 提供。使用解析器,它將節點反序列化為列表或映射,具體取決於我們提供的 TypeReference。
4.2. 使用 convertValue()
同樣,我們可以使用 convertValue() 方法:
List<Person> convertValueJsonNodeToList(JsonNode personsNode) {
TypeReference<List<Person>> typeReferenceList = new TypeReference<List<Person>>() {};
return new ObjectMapper().convertValue(personsNode, typeReferenceList);
}
Map<String, Person> convertValueJsonNodeToMap(JsonNode idToPersonsNode) {
TypeReference<Map<String, Person>> typeReferenceMap = new TypeReference<Map<String, Person>>() {};
return new ObjectMapper().convertValue(idToPersonsNode, typeReferenceMap);
}
convertValue() 方法的工作原理是首先序列化輸入對象,然後將其反序列化為所需類型。 因此,它更靈活地可用於從一個 Java 對象轉換為 JsonNode。
5. 自定義反序列器
我們還可以提供自定義反序列器來執行轉換。 讓我們看看如何定義它:
public class CustomPersonListDeserializer extends JsonDeserializer<List<Person>> {
@Override
public List<Person> deserialize(com.fasterxml.jackson.core.JsonParser p,
com.fasterxml.jackson.databind.DeserializationContext ctxt) throws IOException {
ObjectMapper objectMapper = (ObjectMapper) p.getCodec();
List<Person> personList = new ArrayList<>();
JsonNode personsNode = objectMapper.readTree(p);
for (JsonNode node : personsNode) {
personList.add(objectMapper.readValue(node.traverse(), Person.class));
}
return personList;
}
}
讓我們看看代碼中的一些重要部分:
- 首先,該類繼承 Jackson 的 JsonDeserializer。
- 然後,我們覆蓋 deserialize() 方法並提供我們的實現。
- 在實現中,我們從 JsonParser 對象中獲取 ObjectMapper。
- objectMapper.readTree() 將解析器表示的所有樹轉換為 JsonNode 實例。
- 最後,類似於手動轉換,我們通過循環遍歷節點將 JSON 數組中的每個節點轉換為 Person 對象。
該反序列器與其它方法類似,但它可以提供分層關注。 此外,自定義反序列器提供了靈活性,因為我們可以輕鬆地在調用代碼中切換反序列器。
我們將研究如何在測試代碼的下一部分中使用該反序列器。
6. 測試
現在我們已經準備好不同的方法,讓我們編寫一些測試來驗證它們。
6.1. 設置
讓我們設置測試類:
public class JsonNodeToCollectionUnitTest {
public static String jsonString = "{\"persons\":[{\"name\":\"John\",\"age\":30},{\"name\":\"Alice\",\"age\":25}],\"idToPerson\":{\"1234\":{\"name\":\"John\",\"age\":30},\"1235\":{\"name\":\"Alice\",\"age\":25}}}";
static JsonNode completeJson;
static JsonNode personsNode;
static JsonNode idToPersonNode;
@BeforeAll
static void setup() throws JsonProcessingException {
completeJson = new ObjectMapper().readTree(jsonString);
personsNode = completeJson.get("persons");
idToPersonNode = completeJson.get("idToPerson");
}
}
這裏,我們定義一個我們可以用作測試輸入的 JSON 字符串。然後,我們定義一個 setup() 方法,該方法在所有測試執行之前執行。它設置我們的輸入 JsonNode 對象。
6.2. 測試轉換方法
接下來,讓我們測試我們的轉換方法:
@Test
void givenJsonNode_whenConvertingToList_thenFieldsAreCorrect() throws IOException {
List<Person> personList1 = JsonNodeConversionUtil.manualJsonNodeToList(personsNode);
List<Person> personList2 = JsonNodeConversionUtil.readValueJsonNodeToList(personsNode);
List<Person> personList3 = JsonNodeConversionUtil.convertValueJsonNodeToList(personsNode);
validateList(personList1);
validateList(personList2);
validateList(personList3);
}
private void validateList(List<Person> personList) {
assertEquals(2, personList.size());
Person person1 = personList.get(0);
assertEquals("John", person1.getName());
assertEquals(30, person1.getAge());
Person person2 = personList.get(1);
assertEquals("Alice", person2.getName());
assertEquals(25, person2.getAge());
}
這裏,我們調用每個方法,並驗證返回列表的內容是否符合我們的預期。
讓我們為映射轉換添加一個類似的測試:
@Test
void givenJsonNode_whenConvertingToMap_thenFieldsAreCorrect() throws IOException {
Map<String, Person> personMap1 = JsonNodeConversionUtil.manualJsonNodeToMap(idToPersonNode);
Map<String, Person> personMap2 = JsonNodeConversionUtil.readValueJsonNodeToMap(idToPersonNode);
Map<String, Person> personMap3 = JsonNodeConversionUtil.convertValueJsonNodeToMap(idToPersonNode);
validateMapOfPersons(personMap1);
validateMapOfPersons(personMap2);
validateMapOfPersons(personMap3);
}
private void validateMapOfPersons(Map<String, Person> map) {
assertEquals(2, map.size());
Person person1 = map.get("1234");
assertEquals("John", person1.getName());
assertEquals(30, person1.getAge());
Person person2 = map.get("1235");
assertEquals("Alice", person2.getName());
assertEquals(25, person2.getAge());
}
6.3. 測試自定義反序列器
讓我們看看如何使用自定義反序列器,然後比較結果:
@Test
void givenJsonNode_whenConvertingToListWithCustomDeserializer_thenFieldsAreCorrect() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(List.class, new CustomPersonListDeserializer());
objectMapper.registerModule(module);
List<Person> personList = objectMapper.convertValue(personsNode, new TypeReference<List<Person>>() {
});
validateList(personList);
}
這裏,我們首先用 SimpleModule 將 CustomPersonListDeserializer 封裝在一個模塊中。然後,我們使用 ObjectMapper 註冊此模塊。通過這樣做,我們告訴 ObjectMapper 在 List 需要進行反序列化時,使用 CustomPersonListDeserializer 。當我們將 convertValue() 方法調用,反序列化器將被使用以返回輸出列表。
7. 結論
在本文中,我們探討了如何將 Jackson 的 JsonNode 轉換為帶類型 Java 集合,例如 List 和 Map。我們討論了多種執行此操作的方法,並驗證它們都提供相同的結果。
對於小規模的轉換,我們可以使用手動方法。隨着 JSON 大小的增長,使用 Jackson 庫中的方法會更好。如果需要更復雜的轉換,則最好提供自定義的解序列化器。