背景
最近同事在使用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信息正确使用子类进行反序列化。
戳下面阅读
👇
点亮再看哦👇