將Jackson JsonNode 轉換為類型化集合

Jackson
Remote
1
07:00 PM · Nov 30 ,2025

1. 概述

在本教程中,我們將探索如何將 Jackson 的原始數據類型 JsonNode 轉換為類型為 Java 集合的不同方法。雖然我們可以使用 JsonNode 本身讀取 JSON 數據,但將其轉換為 Java 集合可能會有所幫助。Java 集合比原始 JSON 數據具有優勢,例如類型安全、更快的處理速度以及更多類型特定的操作。

2. 示例設置

在我們的代碼示例中,我們將探討如何將 JsonNode 轉換為 ListMap 對象列表。讓我們設置示例的構建塊。

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) 方法從節點中獲取 nameageJsonNode 提供了各種方法來將返回的值轉換為原始 Java 類型。在這裏,asText()asInt() 方法將值轉換為 Stringint, 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 將其轉換為 ListMap

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() 方法,其中 JsonParserjsonNode.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 集合,例如 ListMap。我們討論了多種執行此操作的方法,並驗證它們都提供相同的結果。

對於小規模的轉換,我們可以使用手動方法。隨着 JSON 大小的增長,使用 Jackson 庫中的方法會更好。如果需要更復雜的轉換,則最好提供自定義的解序列化器。

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

發佈 評論

Some HTML is okay.