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;
}
}
我們的 Item 的 User 屬性現在將是類型 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 實體圖,並對映射過程擁有完全控制權。