知識庫 / JSON / Jackson RSS 訂閱

將Jackson JsonNode 轉換為類型化集合

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

1. 概述

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

2. 示例設置

在我們的代碼示例中,我們將探討如何將 <em >JsonNode</em >> 轉換為 <em >List</em >><em >Map</em >> 對象列表。讓我們設置示例的基礎構建塊。

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 對象:

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);
}

The 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;
    }
}

讓我們來研究一下代碼中的幾個重要部分:

  1. 首先,該類繼承了 Jackson 的 JsonDeserializer
  2. 然後,我們覆蓋了 deserialize() 方法並提供我們的實現。
  3. 在實現中,我們從 JsonParser 對象中獲取 ObjectMapper
  4. objectMapper.readTree() 將解析器表示的所有樹轉換為 JsonNode 實例。
  5. 最後,類似於手動轉換,我們通過循環遍歷節點,將 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 的 <em >JsonNode</em> 轉換為帶類型 Java 集合,例如 <em >List</em><em >Map</em>。我們討論了多種執行此操作的方法,並驗證了它們都提供相同的結果。

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

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

發佈 評論

Some HTML is okay.