背景
最近同事在使用Jackson做列表元素多態的反序列化時,遇到個有意思的問題,這里來記錄下,以便后面使用時方便查找。具體場景,我們且來看下面代碼:
@Data
static class Animal {
private String name;
}
@Data
static class Dog extends Animal {
private int age;
}
@Data
static class Cat extends Animal {
private boolean isCute;
}
@Data
static class ViewDO{
private List<Animal> animals;
}
public static void main(String[] args) throws IOException {
// 1.構建view對象
ViewDO viewDO = new ViewDO();
List<Animal> animals = new ArrayList<>();
viewDO.animals = animals;
// 2.添加Dog實例
Dog dog = new Dog();
dog.setName("Buddy");
dog.setAge( 3);
animals.add(dog);
// 3.添加Cat實例
Cat cat = new Cat();
cat.setName( "Whiskers");
cat.setCute(true);
animals.add(cat);
// 4.序列化ViewDO
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String json = objectMapper.writeValueAsString(viewDO);
System.out.println(json);
// 5.反序列化
viewDO = objectMapper.readValue(json, ViewDO.class);
System.out.println(objectMapper.writeValueAsString(viewDO));
}
運行上面代碼,步驟4輸出為:
{"animals":[{"name":"Buddy","age":3},{"name":"Whiskers","cute":true}]}
步驟5輸出為:
{"animals":[{"name":"Buddy"},{"name":"Whiskers"}]}
根據步驟5我們可以知道反序列化后只保留了父類Animal的name屬性,其子類Dog和Cat的獨有屬性被丟掉了。
解決
其原因也容易理解,就是因為反序列化時ViewDO中的animals的列表元素類型為Animal,jackson并不感知其子類Dog和Cat的存在。要解決這個問題,需要再序列化時把元素的類型信息保存到序列化結果中,這樣反序列化時,就知道當前元素使用那個子類進行反序列化。
為實現上面效果,我們只需要對ViewDO 類改造如下:
@Data
static class ViewDO{
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME, // 使用類名作為類型標識
include = JsonTypeInfo.As.PROPERTY, // 將類型信息作為 JSON 屬性
property = "@class" // 類型信息的字段名為 "@class"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"), // 子類 Dog
@JsonSubTypes.Type(value = Cat.class, name = "cat")}
)
private List<Animal> animals;
}
如上我們添加了 @JsonTypeInfo和@JsonSubTypes注解,添加后,會在序列化時,在序列化結果內添加一個@class屬性,屬性的值為dog或者cat。加上注解后,步驟4序列化的結果為:
{"animals":[{"@class":"dog","name":"Buddy","age":3},{"@class":"cat","name":"Whiskers","cute":true}]}
步驟5反序列化結構為:
{"animals":[{"@class":"dog","name":"Buddy","age":3},{"@class":"cat","name":"Whiskers","cute":true}]}
可知反序列化時可感知子類了,可根據@class信息正確使用子類進行反序列化。