Java中Jackson的多态反序列化详解

 更新时间:2023年11月06日 10:32:59   作者:杨某人信了你的邪  
这篇文章主要介绍了Java中Jackson的多态反序列化详解,多态序列化与反序列化,主要是借助于Jackson的@JsonTypeInfo与@JsonSubTypes注解实现,下面将通过几个例子来简述其运用,需要的朋友可以参考下

Jackson多态反序列化

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

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

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

1、通过类型判断

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

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@Data
public class Person {
    protected Integer type;
}

测试

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

输出

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

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

@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
@Data
public class Person {
    protected Integer type;
}

测试

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

输出

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

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

测试

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

输出

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

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

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

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

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

测试

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

输出

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

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

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

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

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

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.10.2</version>
</dependency>
// 获取该路径下所有类
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);
    }
}

到此这篇关于Java中Jackson的多态反序列化详解的文章就介绍到这了,更多相关Jackson多态反序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mybatis之类型处理器TypeHandler的作用与自定义方式

    Mybatis之类型处理器TypeHandler的作用与自定义方式

    这篇文章主要介绍了Mybatis之类型处理器TypeHandler的作用与自定义方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • SpringBoot项目开发常用技术整合

    SpringBoot项目开发常用技术整合

    今天给大家分享springboot项目开发常用技术整合,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-08-08
  • 详解Java读取本地文件并显示在JSP文件中

    详解Java读取本地文件并显示在JSP文件中

    这篇文章主要介绍了详解Java读取本地文件并显示在JSP文件中的相关资料,这里提供实例帮助大家实现这样的功能,希望能帮助到大家,需要的朋友可以参考下
    2017-08-08
  • spring IOC容器管理必须知道的一些操作(基于XML方式)

    spring IOC容器管理必须知道的一些操作(基于XML方式)

    Spring框架的核心是Spring容器,容器创建对象,将它们装配在一起,配置它们并管理它们的完整生命周期,下面这篇文章主要给大家介绍了关于spring IOC容器管理必须知道的一些操作,需要的朋友可以参考下
    2022-03-03
  • 浅析Java 对象引用和对象本身

    浅析Java 对象引用和对象本身

    这篇文章主要介绍了Java 对象引用和对象本身的相关资料,帮助大家更好的理解和学习Java,感兴趣的朋友可以了解下
    2020-08-08
  • Spring Bean六种作用域的适用场景和使用方式

    Spring Bean六种作用域的适用场景和使用方式

    本文主要介绍了Spring Bean六种作用域的适用场景和使用方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Java实现圆形碰撞检测

    Java实现圆形碰撞检测

    这篇文章主要为大家详细介绍了Java实现圆形碰撞检测,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • Java中Spring的创建和使用详解

    Java中Spring的创建和使用详解

    这篇文章主要介绍了Java中Spring的创建和使用详解,Spring 是⼀个包含了众多⼯具⽅法的 IoC 容器,既然是容器那么 它就具备两个最基本的功能,将对象存储到容器中,从容器中将对象取出来,需要的朋友可以参考下
    2023-08-08
  • 浅析关于java的序列化和反序列化

    浅析关于java的序列化和反序列化

    这篇文章主要介绍了浅析关于java的序列化和反序列化,所谓序列化,就是把要传输的对象以及相关信息转换成字节数组进行存储的过程,而反序列化就是将字节数组再转回对象的过程,需要的朋友可以参考下
    2023-07-07
  • Java多线程之循环栅栏技术CyclicBarrier使用探索

    Java多线程之循环栅栏技术CyclicBarrier使用探索

    这篇文章主要介绍了Java多线程之循环栅栏技术CyclicBarrier,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪<BR>
    2024-01-01

最新评论