• Jackson多态反序列化


    多态序列化与反序列化,主要是借助于Jackson的@JsonTypeInfo@JsonSubTypes注解实现,下面将通过几个例子来简述其运用。

    首先,创建几个基本的实体类。这些类都比较简单,有共同的属性也有不同的属性,这里为了简单,共同属性就只有一个type。

    @Data
    public class Person {
        protected Integer type;
    }
    
    • 1
    • 2
    • 3
    • 4
    @EqualsAndHashCode(callSuper = true)
    @Data
    public class Student extends Person {
        private String studentName;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    @EqualsAndHashCode(callSuper = true)
    @Data
    public class Teacher extends Person {
        private String teacherName;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    @EqualsAndHashCode(callSuper = true)
    @Data
    public class Doctor extends Person{
        private String doctorName;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1、通过类型判断

    在父类上加如下注解。因为是通过类型进行序列化,所以只需要加一个注解就够了。

    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
    @Data
    public class Person {
        protected Integer type;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试

    @Test
    void test1() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Student student = new Student();
        student.setType(0);
        student.setStudentName("三好学生~");
        System.out.println(objectMapper.writeValueAsString(student));
        String json1 = "{\"@class\":\"com.example.jackson.Student\",\"studentName\":\"三好学生~\",\"type\":null}";
        String json2 = "{\"@class\":\"com.example.jackson.Teacher\",\"teacherName\":\"十佳教师~\",\"type\":null}";
        System.out.println(objectMapper.readValue(json1, Person.class));
        System.out.println(objectMapper.readValue(json2, Person.class));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出

    {"@class":"com.example.jackson.Student","type":0,"studentName":"三好学生~"}
    Student(studentName=三好学生~)
    Teacher(teacherName=十佳教师~)
    
    • 1
    • 2
    • 3

    如果不想用@class做为类型标识,也可使用简略模式

    @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
    @Data
    public class Person {
        protected Integer type;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试

    @Test
    void test2() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Student student = new Student();
        student.setType(0);
        student.setStudentName("三好学生~");
        System.out.println(objectMapper.writeValueAsString(student));
        String json1 = "{\"@c\":\".Student\",\"studentName\":\"三好学生~\",\"type\":null}";
        String json2 = "{\"@c\":\".Teacher\",\"teacherName\":\"十佳教师~\",\"type\":null}";
        System.out.println(objectMapper.readValue(json1, Person.class));
        System.out.println(objectMapper.readValue(json2, Person.class));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出

    {"@c":".Student","type":0,"studentName":"三好学生~"}
    Student(studentName=三好学生~)
    Teacher(teacherName=十佳教师~)
    
    • 1
    • 2
    • 3

    2、通过属性值判断

    上面的实体类中,存在共同属性,比如type就是一个共同属性,可以通过这个共同属性的值来判断需要反序列化为哪一个类。

    如下,当选择属性为type,当值为1时反序列化为Student,值为2时反序列化为Teacher,值为3或4则反序列化为Doctor

    因为type是类中已存在的属性,所以@JsonTypeInfo注解中的include属性设置为EXISTING_PROPERTY,否则序列化的时候不管原先类中有没有type属性,都会在序列化后的json中添加一个type,出现一个json中有两个相同属性的情况,这里就不贴出来了,感兴趣可以自己去试一下。

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY,
            property = "type", visible = true)
    @JsonSubTypes(value = {
            @JsonSubTypes.Type(value = Student.class, name = "1"),
            @JsonSubTypes.Type(value = Teacher.class, name = "2"),
            @JsonSubTypes.Type(value = Doctor.class, names = {"3", "4"})
    })
    @Data
    public class Person {
        protected Integer type;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    测试

    @Test
    void test3() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Student student = new Student();
        student.setType(1);
        student.setStudentName("三好学生~");
        System.out.println(objectMapper.writeValueAsString(student));
        String json1 = "{\"studentName\":\"三好学生~\",\"type\":1}";
        String json2 = "{\"teacherName\":\"十佳教师~\",\"type\":2}";
        String json3 = "{\"doctorName\":\"华佗在世~\",\"type\":4}";
        System.out.println(objectMapper.readValue(json1, Person.class));
        System.out.println(objectMapper.readValue(json2, Person.class));
        System.out.println(objectMapper.readValue(json3, Person.class));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    输出

    {"type":1,"studentName":"三好学生~"}
    Student(studentName=三好学生~)
    Teacher(teacherName=十佳教师~)
    Doctor(doctorName=华佗在世~)
    
    • 1
    • 2
    • 3
    • 4

    只不过使用这种方法有弊端,首先就是需要将涉及到反序列化的所有子类都标注到基类上便于基类感知,如果子类多了,那就显得臃肿,而且也违反了开闭原则。所以介绍下面的另一种方式

    通过属性值判断,除了上面的使用@JsonSubTypes在基类上全都列出来之外,还可以使用@JsonTypeName注解标注在子类上,如下

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY,
            property = "type", visible = true)
    @Data
    public class Person {
        protected Integer type;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    @EqualsAndHashCode(callSuper = true)
    @Data
    @JsonTypeName("1")
    public class Student extends Person {
        private String studentName;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其他几个类也是一样,就不贴出来了。

    测试

    @Test
    void test4() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
    
        // 关键的是这行
        objectMapper.registerSubtypes(Student.class, Teacher.class, Doctor.class);
    
        Student student = new Student();
        student.setType(1);
        student.setStudentName("三好学生~");
        System.out.println(objectMapper.writeValueAsString(student));
        String json1 = "{\"studentName\":\"三好学生~\",\"type\":1}";
        String json2 = "{\"teacherName\":\"十佳教师~\",\"type\":2}";
        String json3 = "{\"doctorName\":\"华佗在世~\",\"type\":3}";
        System.out.println(objectMapper.readValue(json1, Person.class));
        System.out.println(objectMapper.readValue(json2, Person.class));
        System.out.println(objectMapper.readValue(json3, Person.class));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    输出

    Student(studentName=三好学生~)
    Teacher(teacherName=十佳教师~)
    Doctor(doctorName=华佗在世~)
    
    • 1
    • 2
    • 3

    通过ObjectMapperregisterSubtypes方法注册子类,这样一来就不需要在基类上标注@JsonSubTypes注解了。

    当然,除了使用@JsonTypeName注解,然后直接注册这个类之外,还可以通过下面的方式进行注册,效果是一样的

    objectMapper.registerSubtypes(new NamedType(Student.class, "1"));
    
    • 1

    上面的代码是演示,所以在注册时是一个个写死的,但实际上,不可能将所有类全都写出来进行注册,实际上可以借助一些工具进行来获取所有的子类,比如Reflections

    <dependency>
        <groupId>org.reflectionsgroupId>
        <artifactId>reflectionsartifactId>
        <version>0.10.2version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    // 获取该路径下所有类
    Reflections reflections = new Reflections("com.example");
    // 获取对应类的所有子类
    Set<Class<? extends Person>> classSet = reflections.getSubTypesOf(Person.class);
    for (Class<? extends Person> clazz : classSet) {
        // 将带有@JsonTypeName注解的类进行注册
        if (clazz.isAnnotationPresent(JsonTypeName.class)) {
            objectMapper.registerSubtypes(clazz);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    Python实现企业微信自动打卡程序二:跳过节假日,随机打卡时间,定时任务,失败通知
    秸秆开启黑土地绿色低碳循环经济链 国稻种芯绿色沃土计划
    Springmvc中对请求的处理
    HTML5-画布使用教程
    [倒置字符串]
    docker
    Opencv中的GrabCut图像分割
    《文献阅读》- 遗传算法作为量子近似优化算法的经典优化器(未完成)
    springboot单独在指定地方输出sql
    不用rustup,Windows下gnu版Rust安装与开发环境配置
  • 原文地址:https://blog.csdn.net/Remember_Z/article/details/127875824