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
}當將此內容映射到完全相同的實體時,默認情況下,這當然會失敗:
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"])我們將會通過使用自定義的 Deserializer 進行自定序化來解決這個問題:
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 {
...
}定義了在類級別上的反序列器後,就不需要在 ObjectMapper 上進行註冊——一個默認映射器就足夠了:
Item itemWithOwner = new ObjectMapper().readValue(json, Item.class);這種按類配置在我們可能無法直接訪問原始 ObjectMapper 進行配置的情況下非常有用。
5. 為泛型類型創建自定義反序列器
現在,讓我們創建一個 Wrapper 類,其中包含泛型類型 T 的唯一參數:
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;
}
}這段 JSON 解析器還可以處理同一類中具有不同類型的多個 Wrapper 屬性。 假設我們有一個包含 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 實體圖,並對映射過程擁有完全控制權。