Java中Jackson的多态反序列化详解
作者:杨某人信了你的邪
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多态反序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!