<<踩坑记>>系列--用Jackson解决子类序列化后,反序列化成父类的异常

问题描述

       一个Driver对象,我需要对它进行序列化,Driver对象有一个属性,是一个Car对象,然后Car是一个父类,Tesla(特斯拉)是Car的一个子类,这个子类有父类没有的一个属性,比如叫”chargingTime(充电时间)”

  • Driver 类
1
2
3
4
public class Driver {
private String name; //+get set
private Car car; //+get set
}
  • Car 类
1
2
3
4
5
6
public class Car {
/**
* 品牌
*/

private String brand; //+get set
}
  • Tesla, Car的子类
1
2
3
4
5
6
public class Tesla extends Car {
/**
* 充电时间.
*/

private String chargingTime; //+get set
}

       现在Driver对象下面的Car属性是一个Tesla实例,对这个Driver进行序列化之后,再反序列化,会失败。

  • SerializeTest 单测代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class SerializeTest {

@Test
public void testSerialize() {
Tesla tesla = new Tesla();
tesla.setBrand("Tesla");
tesla.setChargingTime("1 day");

Driver driver = new Driver();
driver.setCar(tesla); //将Car属性值设置为其子类Tesla的实例
driver.setName("water");

ObjectMapper objectMapper = new ObjectMapper();
String result = "";

//序列化
try {
result = objectMapper.writeValueAsString(driver);
} catch (IOException e) {
e.printStackTrace();
}

//反序列化
try {
Driver driver_1 = objectMapper.readValue(result, Driver.class);
//希望得到Tesla对象.
System.out.printf(driver_1.getCar().getClass().getName());
} catch (IOException e) {
e.printStackTrace();
}
}

}

       报错信息是说在Car这个类里面找不到”chargingTime(充电时间)”这个属性。

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field “chargingTime” (Class org.daikaixian.jspider.models.Car), not marked as ignorable

       那么问题来了,这种场景该如何处理了?该如何指定要反序列化的对象(这里的Driver)的某个属性(这里的Car)反序列化成子类(Tesla)而不是类中定义的父类类型(Car).

Demo Code

       google之后又是在StackOverFlow上找到答案,点此查看原文

       其实只需要对父类进行一些特殊的注解,就可以轻松解决问题,修改Car类代码如下:

1
2
3
4
5
6
7
8
9
10
11
12

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME,include=JsonTypeInfo.As.PROPERTY, property="@type")
@JsonSubTypes({
@JsonSubTypes.Type(value=Tesla.class, name="Tesla")
}) //如果有多个子类,可以添加多个,用逗号隔开.

public class Car {
/**
* 品牌
*/

private String brand; //+get , set
}

       
可能因为Jackson版本不同的原因,如果上述改动还不能解决问题,就再对objectMapper进行一些注册操作,就好了.

1
2
3
4
5
6
...
ObjectMapper objectMapper = new ObjectMapper();
//对ObjectMapper进行注册操作.
objectMapper.registerSubtypes(new NamedType(Tesla.class, "Tesla"));
String result = "";
...

总结

       如果自己编码来实现的话,思路可能差不多:先要知道这个父类有哪些子类,然后将序列化的数据按照这些子类的模板一遍遍的反序列化,成功匹配即可.如果成功匹配多个,可能还要考虑匹配顺序的问题.不过既然Jackson提供了成熟的解决方案,那就学会用吧.