Jackson如何多态反序列化

原创
2020/08/02 11:20
阅读数 2K

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());
    }
}

运行示例可以正确反序列

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部