在Springboot + Mybaitis-plus 项目中利用Jackson实现json对java多态的(反)序列化
Jackson允许配置多态类型处理,当JSON面对的转换对象是一个接口、抽象类或者一个基类的时候,可以通过一定配置实现JSON的转换。在实际项目中,Controller层接收入参以及在Dao层将对象以json的形式存入数据库时都可能会遇到这个问题。而Springboot和mp都支持使用Jackson处理json,从而可以利用Jackson的特点,解决这一问题。
注意
为了代码简洁,这里的代码忽略了set和get方法和构造函数
在本例中,父类Zoo有两个子类Dog和Cat类
public static class Zoo {
private String name;
private AnimalTypeEnum animalType;
}
父类Zoo中,包含一个代表动物种类的枚举字段
public enum AnimalTypeEnum {
DOG("dog"),
CAT("cat");
private final String name;
}
对于子类Dog包含一个速度属性
public static class Dog extends Zoo {
private Double speed;
}
对于子类Cat包含一个尺寸属性
public static class Cat extends Zoo {
private Integer size;
}
我们想做的事情是根据Zoo中的动物类型枚举字段animalType,将JSON反序列化为两种子类
方法一
使用Jackson提供的处理注解可以实现上述功能
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "animalType",
visible = true
)
@JsonSubTypes(
{
@JsonSubTypes.Type(value = Dog.class, name = "DOG"),
@JsonSubTypes.Type(value = Cat.class, name = "CAT")
}
)
public static class Zoo {
private String name;
private AnimalTypeEnum animalType;
}
@JsonTypeInfo()该注解表示对该类开启多态类型处理,包含四个属性
use 代表使用哪一种类型识别码
JsonTypeInfo.Id.NAME 是本例中选择的类型识别码,意指一个指定的名字
include代表识别码是如何包含进JSON
JsonTypeInfo.As.EXISTING_PROPERTY 代表POJO中存在的类型
property 指定类型表示码的属性名称"animalType" 就是POJO中的类型字段名
visible 代表类型标识符是否会进入反序列化,默认false由于这里我们同样需要该字段反序列化,所以设置为true
@JsonSubTypes()该注解用于给出给定类的子类
@JsonSubTypes.Type[]数组中给出了多态类和property中指定属性某个值之间的绑定关系。在上例中,Dog类和animalType = DOG的值进行了绑定
在父类Zoo上加入如上注解之后,即可实现多态反序列化
对应测试
public void method1Test() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Cat cat = new Cat("小猫", AnimalTypeEnum.CAT, 20);
Dog dog = new Dog("小狗", AnimalTypeEnum.DOG, 30.03);
String catJson = objectMapper.writeValueAsString(cat);
String dogJson = objectMapper.writeValueAsString(dog);
log.debug(catJson);
log.debug(dogJson);
//反序列化
Zoo catZoo = objectMapper.readValue(catJson, Zoo.class);
Zoo dogZoo = objectMapper.readValue(dogJson, Zoo.class);
//类型一致
assertEquals(catZoo.getClass(),cat.getClass());
assertEquals(dogZoo.getClass(),dog.getClass());
//参数值一致
assertEquals(20,((Cat)catZoo).getSize());
assertEquals(30.03,((Dog)dogZoo).getSpeed());
}

可以看到,经过添加注解可以实现我们的需求
这样不管是springboot还是mybaitis-plus进行反序列化的时候,都通过注解的信息按照我们的要求进行反序列化
方法二
在项目中,一个基类会有很多的子类,并且随着项目的深入,子类可能会越来越多。使用上面的方法,需要不停的添加@JsonSubTypes中的内容,十分繁琐。这种写法是 违反开闭原则(OCP)的。
通过阅读源码,我们可以看到,其多态处理的基本原理就是将子类何其对应的名称之间的绑定关系注册到ObjectMapper中。
方法二的思路是给每个子类增加一个注解@JsonTypeName(value = ""),然后通过扫描所有带有注解的类,将所有带有标记的类注册到ObjectMapper中。
在Springboot中自定义ObjectMapper有很多办法,可以参考在SpringBoot中自定义 Jackson的ObjectMapper
首先生成一个ObjectMapper 的bean
@Configuration
public class ObjectMapperConfig {
@Bean
@Primary
//使这个bean优先被注入
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
//使用reflection框架,获取本包下的所有带有@JsonTypeName的注解
Reflections reflections = new Reflections("cn.");
Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(JsonTypeName.class);
//将这个上面扫描得到的类注册进这个ObjectMapper中
objectMapper.registerSubtypes(classSet);
//这里是将我们定义好的objectMapper set 进 Mybaitis-plus的Jackson处理器中,从而使得MP也可以 顺利的进行反序列化
JacksonTypeHandler.setObjectMapper(objectMapper);
return objectMapper;
}
}
父类只需要添加这样一个注解
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "animalType",
visible = true
)
public static class Zoo {
private String name;
private AnimalTypeEnum animalType;
}
子类添加注解
@JsonTypeName("DOG")
public static class Dog extends Zoo {
private Double speed;
}
方法三
在我们的场景中,分类标识符是一个枚举类型。因此,我们希望将所有的子类和标识符名称对应的信息全部放在该枚举类中,使得仅通过枚举类就可以绑定好所有子类和名称之间的关系。
定义一个接口和注解,并在接口上使用了这个注解
@JsonSubTypeEnum.JsonSubTypeAnnotation
public interface JsonSubTypeEnum {
Class<?> getJsonType();
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface JsonSubTypeAnnotation {
}
}
这个接口定义了一个获取子类类信息的方法
public enum AnimalType implements JsonSubTypeEnum {
DOG(Dog.class),
CAT(Cat.class),
;
private final Class<? extends Animal> animalClass;
@Override
public Class<?> getJsonType() {
return this.animalClass;
}
}
让需要用于分类的枚举实现这个接口,枚举中的animalClass属性,用来记录该标识符对应的子类的类别。
再来看ObjectMapper bean
@Bean
@Primary
public static ObjectMapper getObjectMapper(){
ObjectMapper objectMapper = new ObjectMapper();
Reflections reflections = new Reflections("com.");
//获取所有带有自定义注解的类
Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(JsonSubTypeEnum.JsonSubTypeAnnotation.class);
//将其中的枚举类拿出来处理
for (Class<?> enumTyp : classSet) {
if (!enumTyp.isEnum()) {
continue;
}
final Object[] enumConstants = enumTyp.getEnumConstants();
for (Object e : enumConstants) {
if (e instanceof JsonSubTypeEnum) {
//将每个子类和标识符绑定注册进入objectMapper
final Class<?> subType = ((JsonSubTypeEnum) e).getJsonType();
final String name = ((Enum<?>) e).name();
objectMapper.registerSubtypes(new NamedType(subType, name));
}
}
}
//这里是将我们定义好的objectMapper set 进 Mybaitis-plus的Jackson处理器中,从而使得MP也可以 顺利的进行反序列化
JacksonTypeHandler.setObjectMapper(objectMapper);
return objectMapper;
}
在Springboot + Mybaitis-plus 项目中利用Jackson实现json对java多态的(反)序列化的更多相关文章
- Spring学习---Spring中利用jackson进行JSON转换
Spring中利用jackson进行JSON转换 import java.util.List; import com.fasterxml.jackson.core.JsonProcessingExce ...
- Java下利用Jackson进行JSON解析和序列化
Java下利用Jackson进行JSON解析和序列化 Java下常见的Json类库有Gson.JSON-lib和Jackson等,Jackson相对来说比较高效,在项目中主要使用Jackson进行 ...
- 【转载】Java下利用Jackson进行JSON解析和序列化
参考资料: https://blog.csdn.net/sdut406/article/details/85647982 Java下常见的Json类库有Gson.JSON-lib和Jackson等,J ...
- Java下利用Jackson进行JSON解析和序列化1
Java下常见的Json类库有Gson.JSON-lib和Jackson等,Jackson相对来说比较高效,在项目中主要使用Jackson进行JSON和Java对象转换,下面给出一些Jackson的J ...
- 剑指Offer——企业级项目中分层的含义与依据及多态的优势
剑指Offer--企业级项目中分层的含义与依据及多态的优势 关于以上两点,由于项目经验较少,自己不是很明白,特整理如下. 常见分层架构模式 三层架构 3-tier architecture 微 ...
- Spring-boot 项目中使用 jackson 遇到的一个问题
jackson介绍 java代码中实现序列化和反序列化的工具类 jackson使用Demo https://github.com/Naylor55/JavaDebrisCode/tree/branch ...
- 如何在我们项目中利用开源的图表(js chart)
最近觉得应该把自己在技术上的一些心得记录在博客里面跟大家分享,一起讨论,一起成长! 这篇随笔主要为介绍chart在项目中的运用,因为在我们看到一些开源的chart时候,是使 ...
- 在项目中利用TX Text Control进行WORD文档的编辑显示处理
在很多文档管理的功能模块里面,我们往往需要对WORD稳定进行展示.编辑等处理,而如果使用微软word控件进行处理,需要安装WORD组件,而且接口使用也不见得简单易用,因此如果有第三方且不用安装Offi ...
- vue项目中利用popstate处理页面返回操作
需求背景:项目中需要做一个返回确认,避免用户误触返回键而退出当前页面. 原理:利用history和浏览器刷新popstate状态 实现: 1.在mounted() 阶段判断并添加popstate事件监 ...
随机推荐
- CVPR2020最新论文扫描盘点(上)
CVPR2020最新论文扫描盘点(上) 最近计算机视觉三大顶会之一CVPR2020接收结果已经公布,一共有1470篇论文被接收,接收率为22%,相比去年降低3个百分点,竞争越来越激烈.这里整理来自Tw ...
- Django框架之路由层汇总
一 Django中路由的作用 URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于客户端发来 ...
- Java基础_循环嵌套_打印乘法口诀、菱形,各种图形,计算二元一次和三元一次方程组_7
循环嵌套 打印乘法口诀 for(int j=1;j<=9;j++){ for(int i=1;i<=j;i++){ System.out.print(i+"*"+j+& ...
- linux用户组添加和权限的设置
1.useradd 添加用户 useradd [选项]... 用户名 -u 用户id.-d 家目录路径.-s 登录Shell(解释器).-G 附加组 /sbin/nologin :禁止用户登陆系统 ...
- 【C++】map容器的用法
检测map容器是否为空: 1 #include <iostream> 2 #include<map> 3 #include<string> 4 using name ...
- 【UG二次开发】创建、查询、编辑成型特征的函数
创建成型特征函数UF_MODL_create_boss 通过设置凸台的参数建立凸台特征UF_MODL_create_rect_pad 通过设置矩形凸垫的参数建立矩形凸垫特征UF_MODL_create ...
- Spring Boot WebFlux-01——WebFlux 快速入门实践
第01课:WebFlux 快速入门实践 Spring Boot 2.0 spring.io 官网有句醒目的话是: BUILD ANYTHING WITH SPRING BOOT Spring Boot ...
- 【模板】 RMQ求区间最值
RMQ RMQ简单来说就是求区间的最大值(最小值) 核心算法:动态规划 RMQ(以下以求最大值为例) F[i,j]表示 从 i 开始 到i+2j -1这个区间中的最大值 状态转移方程 F[i,j]=m ...
- SSM动态展示分页
这个作业属于哪个课程 2021春软件工程实践|S班(福州大学) 这个作业要求在哪里 作业具体要求 这个作业的目标 个人技术 参考文献 ... 目录 技术概述 技术详述 问题和解决过程 总结 参考文献 ...
- js笔记22
1.在拖拽元素的时候,如果元素的内部加了文字或者图片,拖拽效果会失灵? 浏览器会给文字和图片一个默认行为,当文字和图片被选中的时候,会有一个拖拽的效果,即使我们没有人为给他添加.所以当我们点击这个元素 ...