知識庫 / JSON / Jackson RSS 訂閱

使用 Jackson 映射嵌套值

Data,Jackson
HongKong
7
09:54 PM · Dec 05 ,2025

1. 概述

使用JSON時,常見的用例是從一個模型轉換到另一個模型。例如,我們可能希望將複雜的、深層嵌套的對象圖轉換成更簡單、更易於在其他領域使用的模型。

在本快速教程中,我們將探討如何使用 Jackson 將嵌套值映射出來,從而扁平化複雜的結構。我們將以三種不同的方式反序列化JSON:

  • 使用 @JsonProperty
  • 使用 JsonNode
  • 使用自定義 JsonDeserializer

2. Maven 依賴

讓我們首先將以下依賴添加到 <em pom.xml</em> 中:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.17.2</version>
</dependency>

我們可以在 jackson-databind 的最新版本上找到 Maven Central

3. JSON 來源

請考慮以下 JSON 作為我們示例的源材料。

雖然結構是人為構造的,但請注意,我們包含嵌套兩層深度的屬性:

{
    "id": "957c43f2-fa2e-42f9-bf75-6e3d5bb6960a",
    "name": "The Best Product",
    "brand": {
        "id": "9bcd817d-0141-42e6-8f04-e5aaab0980b6",
        "name": "ACME Products",
        "owner": {
            "id": "b21a80b1-0c09-4be3-9ebd-ea3653511c13",
            "name": "Ultimate Corp, Inc."
        }
    }  
}

4. 簡化領域模型

在以下描述的扁平化領域模型中,使用 <em >Product </em >> 類,我們將提取brandName >,該字段位於源 JSON 中嵌套的一層內部。

此外,我們還將提取 <em >ownerName </em >>,該字段嵌套了兩層,位於嵌套的brand > 對象內部:

public class Product {

    private String id;
    private String name;
    private String brandName;
    private String ownerName;

    // standard getters and setters
}

5. 使用註解進行映射

要映射嵌套的 屬性,首先需要將嵌套的 對象解包為 ,並提取 屬性。要映射 ,需要將嵌套的 對象解包為 ,並提取其 屬性。

我們可以指示 Jackson 使用 組合使用 @JsonProperty 註解和自定義邏輯來解包嵌套屬性,這些自定義邏輯我們會添加到我們的 類中:

public class Product {
    // ...

    @SuppressWarnings("unchecked")
    @JsonProperty("brand")
    private void unpackNested(Map<String,Object> brand) {
        this.brandName = (String)brand.get("name");
        Map<String,String> owner = (Map<String,String>)brand.get("owner");
        this.ownerName = owner.get("name");
    }
}

我們的客户端代碼現在可以使用 ObjectMapper 將我們的源 JSON 轉換為,該 JSON 存儲為測試類中 String 常量 SOURCE_JSON

@Test
public void whenUsingAnnotations_thenOk() throws IOException {
    Product product = new ObjectMapper()
      .readerFor(Product.class)
      .readValue(SOURCE_JSON);

    assertEquals(product.getName(), "The Best Product");
    assertEquals(product.getBrandName(), "ACME Products");
    assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}

6. 使用 JsonNode 進行映射

使用 JsonNode 對嵌套數據結構進行映射需要進行一些額外的操作。

這裏我們使用 ObjectMapperreadTree 方法解析出所需的字段:

@Test
public void whenUsingJsonNode_thenOk() throws IOException {
    JsonNode productNode = new ObjectMapper().readTree(SOURCE_JSON);

    Product product = new Product();
    product.setId(productNode.get("id").textValue());
    product.setName(productNode.get("name").textValue());
    product.setBrandName(productNode.get("brand")
      .get("name").textValue());
    product.setOwnerName(productNode.get("brand")
      .get("owner").get("name").textValue());

    assertEquals(product.getName(), "The Best Product");
    assertEquals(product.getBrandName(), "ACME Products");
    assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}

7. 使用自定義 JsonDeserializer 進行映射

使用自定義 JsonDeserializer 映射嵌套數據結構與從實現角度來看,使用 JsonNode 的方法完全相同。

首先,創建 JsonDeserializer

public class ProductDeserializer extends StdDeserializer<Product> {

    public ProductDeserializer() {
        this(null);
    }

    public ProductDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Product deserialize(JsonParser jp, DeserializationContext ctxt) 
      throws IOException, JsonProcessingException {
 
        JsonNode productNode = jp.getCodec().readTree(jp);
        Product product = new Product();
        product.setId(productNode.get("id").textValue());
        product.setName(productNode.get("name").textValue());
        product.setBrandName(productNode.get("brand")
          .get("name").textValue());
        product.setOwnerName(productNode.get("brand").get("owner")
          .get("name").textValue());		
        return product;
    }
}

7.1. 手動註冊反序列器

要手動註冊我們的自定義反序列器,客户端代碼必須將 <em >JsonDeserializer</em> 添加到 <em >Module</em> 中,將 <em >Module</em> 註冊到 <em >ObjectMapper</em> 中,並調用 <em >readValue</em>

@Test
public void whenUsingDeserializerManuallyRegistered_thenOk()
 throws IOException {
 
    ObjectMapper mapper = new ObjectMapper();
    SimpleModule module = new SimpleModule();
    module.addDeserializer(Product.class, new ProductDeserializer());
    mapper.registerModule(module);

    Product product = mapper.readValue(SOURCE_JSON, Product.class);
 
    assertEquals(product.getName(), "The Best Product");
    assertEquals(product.getBrandName(), "ACME Products");
    assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}

7.2. 自動註冊反序列器

作為手動註冊 <em >JsonDeserializer</em> 的替代方案,我們可以 直接在類上註冊反序列器

@JsonDeserialize(using = ProductDeserializer.class)
public class Product {
    // ...
}

採用這種方法,無需手動註冊。

讓我們通過自動註冊查看我們的客户端代碼:

@Test
public void whenUsingDeserializerAutoRegistered_thenOk()
  throws IOException {
 
    ObjectMapper mapper = new ObjectMapper();
    Product product = mapper.readValue(SOURCE_JSON, Product.class);

    assertEquals(product.getName(), "The Best Product");
    assertEquals(product.getBrandName(), "ACME Products");
    assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}

8. 結論

在本文中,我們演示了多種使用 Jackson 解析包含嵌套值的 JSON 的方法。 請參閲我們的主要 Jackson 教程頁面以獲取更多示例。

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

發佈 評論

Some HTML is okay.