1概述
JSON 是一种轻量级的数据交换格式,其简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。 我们平时大都是将单个类序列化成JSON,在将JSON反序列化为单个类,这种场景一般比较简单容易处理,那么如果遇到多态的场景下如何实现呢?下面我们主要看Jackson是如何解决的。
2 简单的JSON序列化与反序列
2.1定义一个User类
public class User {
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
2.2 使用Jackson序列化
public class Main {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
User user = new User("Alice", 22);
//序列化为json
String json = mapper.writeValueAsString(user);
System.out.println(json);
//反序列化为User对象
User decUser = mapper.readValue(json, User.class);
}
}
3 多态场景下JSON的序列化与反序列
上面单个类的序列化与反序列化比较简单,但是在多态的场景下我们如何将JSON串正确的解析并赋值给实际类的父类呢,下面我们举个列子。
3.1 定义一个Car父类接口
public interface Car {
void show();
}
3.2 分别定义两个Car的子类
public class BmCar implements Car {
private String name;
private String color;
public BmCar() {
}
public BmCar(String name, String color) {
this.name = name;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public void show() {
System.out.println(this.name + "_" +this.color);
}
}
public class AdCar implements Car {
private String name;
private String color;
public AdCar() {
}
public AdCar(String name, String color) {
this.name = name;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public void show() {
System.out.println(this.name + "_" +this.color);
}
}
public class CarView {
private Car car;
private String address;
public CarView() {
}
public CarView(Car car, String address) {
this.car = car;
this.address = address;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
3.3 测试
CarView中包含一个Car接口类型字段,我们测试序列化CarViw后然后在反序列化
public class Main {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
CarView view = new CarView(new BmCar("宝马", "白色"), "北京");
String json = mapper.writeValueAsString(view);
System.out.println(json);
CarView decCarView = mapper.readValue(json, CarView.class);
System.out.println(decCarView.getClass());
}
}
运行上面的程序会发现代码执行到第7行进行反序列化的时候报错了,原因是不能实例化一个Car接口,因为Car是一个接口,它有两个子类,反序列的时候无法得知到底需要实例化哪个子类来赋值给父类,这就是多态的场景下反序列的问题,那么该如何解决呢?继续往下看。
3.4 解决方案
Jackson支持多态类型的配置,主要原理就是在json序列化时添加子类标识,那么在反序列化时就知道应该反序列为哪个实例,Jackson支持通过注解@JsonTypeInfo解决。 @JsonTypeInfo 注解说明
- use 表示以那种方式来作为序列化与反序列识别的标识
- include 表示类型元数据的标准类型包含机制
- property 表示具体标识的属性名
@JsonSubTypes 这个注解是结合@JsonTypeInfo一起完成多态反序列化的。上个注解指定了反序列化的标识,而这个注解指定了每个标识对应的子类。 注:当@JsonTypeInfo的use = JsonTypeInfo.Id.CLASS 时不需要配置@JsonSubTypes也可以正确反序列
3.4.1 在Car接口上添加注解
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public interface Car {
void show();
}
再次运行3.3的测试已经可以正确反序列化了
3.5 小技巧
3.4通过注解的方式可以进行多态反序列,但是注解有代码侵入性,如果反序列的接口类是第三方类库,那么就没法将其注解直接添加到类上,此时可以使用Jackson提供的addMinIn方法来解决。 addMixIn(Class<?> target, Class<?> mixinSource) 该方法接受两个参数,表示将mixinSource类上的注解添加到target类或接口上,如下示例
3.5.1 新定义一个MixInCar类
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public class MixInCar {
}
3.5.2 测试
public class Main {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//将MixInCar类上的注解添加到Car接口上
mapper.addMixIn(Car.class, MixInCar.class);
CarView view = new CarView(new BmCar("宝马", "白色"), "北京");
String json = mapper.writeValueAsString(view);
System.out.println(json);
CarView decCarView = mapper.readValue(json, CarView.class);
System.out.println(decCarView.getClass());
}
}
运行示例可以正确反序列