Jackson 定製反序列化入門指南

Data,Jackson
Remote
1
01:02 AM · Dec 01 ,2025

1. 概述

本快速教程將演示如何使用 Jackson 2 將 JSON 反序列化,使用 自定義反序列器

要深入瞭解 Jackson 2 可以做其他酷炫的事情,請訪問主 Jackson 教程。

2. 標準反序列化

我們先定義兩個實體,看看 Jackson 如何在不進行任何自定義的情況下,將 JSON 表示形式反序列化到這些實體中:

public class User {
    public int id;
    public String name;
}
public class Item {
    public int id;
    public String itemName;
    public User owner;
}

現在,讓我們定義我們想要反序列化的 JSON 表示:

{
    "id": 1,
    "itemName": "theItem",
    "owner": {
        "id": 2,
        "name": "theUser"
    }
}

最後,讓我們將此 JSON 反序列化到 Java 實體:

Item itemWithOwner = new ObjectMapper().readValue(json, Item.class);

3. 自定義反序列器於 ObjectMapper

在之前的示例中,JSON 表示與 Java 實體完美匹配。

接下來,我們將簡化 JSON:

{
    "id": 1,
    "itemName": "theItem",
    "createdBy": 2
}

當將此 JSON 映射到完全相同的實體時,默認情況下,這當然會失敗:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: 
Unrecognized field "createdBy" (class org.baeldung.jackson.dtos.Item), 
not marked as ignorable (3 known properties: "id", "owner", "itemName"])
 at [Source: java.io.StringReader@53c7a917; line: 1, column: 43] 
 (through reference chain: org.baeldung.jackson.dtos.Item["createdBy"])

我們將通過執行 自定義反序列化,使用自定義反序列器

public class ItemDeserializer extends StdDeserializer<Item> { 

    public ItemDeserializer() { 
        this(null); 
    } 

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

    @Override
    public Item deserialize(JsonParser jp, DeserializationContext ctxt) 
      throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        int id = (Integer) ((IntNode) node.get("id")).numberValue();
        String itemName = node.get("itemName").asText();
        int userId = (Integer) ((IntNode) node.get("createdBy")).numberValue();

        return new Item(id, itemName, new User(userId, null));
    }
}

正如我們所看到的,反序列器正在使用標準的 Jackson JSON 表示形式 — JsonNode。 將輸入 JSON 表示為 JsonNode 後,我們可以現在 從它提取相關信息並構造我們自己的 Item 實體。

簡單地説,我們需要 註冊此自定義反序列器並正常地解析 JSON:

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Item.class, new ItemDeserializer());
mapper.registerModule(module);

Item readValue = mapper.readValue(json, Item.class);

4. 在類上自定義反序列器或者,我們也可以直接在類上

@JsonDeserialize(using = ItemDeserializer.class)
public class Item {
    ...
}

當反序列器定義在類級別時,無需在上註冊它——默認映射器就可以正常工作:

Item itemWithOwner = new ObjectMapper().readValue(json, Item.class);

這種按類配置的方法在我們無法直接配置時非常有用。

5. 定製化解序列器用於泛型類型

現在,我們創建一個包含泛型類型 T 的唯一參數的 Wrapper 類:

public class Wrapper<T> {

    T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

我們的 ItemUser 屬性現在將是類型 Wrapper<User>

public class Item {
    public int id;
    public String itemName;
    public Wrapper<User> owner;
}

現在,讓我們實現一個自定義解序列器用於這種情況。

首先,我們需要實現 ContextualDeserializer 接口以獲取 Wrapper 內的實體類型。 我們將通過覆蓋 createContextual() 方法來實現這一點。 當此方法被調用時,上下文將得到解決,並且可以通過 BeanProperty 參數檢索 Wrapper 的實際內容。

我們還需要擴展 JsonDeserializer。 因此,我們可以將 Wrapper 的值內部類型設置為 deserialize() 中:

public class WrapperDeserializer extends JsonDeserializer<Wrapper<?>> implements ContextualDeserializer {

    private JavaType type;

    public WrapperDeserializer() {
        // Default constructor
    }

    private WrapperDeserializer(JavaType type) {
        this.type = type;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
        JavaType wrapperType = property.getType().containedType(0);
        return new WrapperDeserializer(wrapperType);
    }

    @Override
    public Wrapper<?> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        Wrapper<?> wrapper = new Wrapper<>();
        wrapper.setValue(deserializationContext.readValue(jsonParser, type));
        return wrapper;
    }
}

這個 JsonDeserializer 也可以處理同一類中具有不同類型的多個 Wrapper 屬性。 假設我們有一個包含具有多個包裝器的類:

public class ItemWithMultipleWrappers {
    private int id;
    private String itemName;
    private Wrapper<User> owner;
    private Wrapper<Integer> count;
}

在這種情況下,解序列化仍然可以工作,因為 createContextual() 方法會創建並返回具有每個屬性正確類型的新的 WrapperDeserializer 實例,而不是直接設置類型字段。

最後,我們需要註冊我們的自定義解序列器才能解序列化 JSON:

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Wrapper.class, new WrapperDeserializer());
mapper.registerModule(module);

Item readValue = mapper.readValue(json, Item.class);

6. 結論

本文介紹瞭如何利用 Jackson 2 讀取非標準 JSON 輸入,以及如何將該輸入映射到任何 Java 實體圖,並對映射過程擁有完全控制權。

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

發佈 評論

Some HTML is okay.