在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());
}
![image-20210613223517026](/Users/liu/Library/Application Support/typora-user-images/image-20210613223517026.png)
可以看到,经过添加注解可以实现我们的需求
这样不管是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事件监 ...
随机推荐
- 自定义 DataLoader
自定义 DataLoader 如 数据输入 一文所介绍,OneFlow 支持两种数据加载方式:直接使用 NumPy 数据或者使用 DataLoader 及其相关算子. 在大型工业场景下,数据加载容易成 ...
- CVPR 2020目标跟踪多篇开源论文(上)
CVPR 2020目标跟踪多篇开源论文(上) 1. SiamBAN:面向目标跟踪的Siamese Box自适应网络 作者团队:华侨大学&中科院&哈工大&鹏城实验室&厦门 ...
- H265与ffmpeg改进开发
H265与ffmpeg改进开发 1. Introduction KSC265是集编码.解码于一体的H.265编解码软件,完全遵循H.265协议标准.符合H.265编码规范的视频都可以通过KSC265进 ...
- matrix_multiply代码解析
matrix_multiply代码解析 关于matrix_multiply 程序执行代码里两个矩阵的乘法,并将相乘结果打印在屏幕上. 示例的主要目的是展现怎么实现一个自定义CPU计算任务. 参考:ht ...
- 为已有数据的DataTable添加一个自增列
/// <summary> /// 为dt表增加一个自增的ID字段 /// </summary> /// <param name="dt">用户 ...
- 【NX二次开发】镜像对象
使用uf5946获取镜像矩阵注意:uf5946镜像这个函数,只能用#define UF_plane_type=46这种类型的数据作为镜像面,不能用#define UF_datum_plane_type ...
- Eclipse安装Pydev插件时所遇到的问题
安装pydev插件折腾了半天,因为总是安装不成功,不成功的表现就是:windows-preferences设置里没有pydev 由于第一次安装这个插件所以不了解安装pydev插件的版本是有要求的,根据 ...
- 使用 vite 构建一个表情选择插件
初始化 Vite 基于原生 ES 模块提供了丰富的内建功能,开箱即用.同时,插件足够简单,它不需要任何运行时依赖,只需要安装 vite (用于开发与构建)和 sass (用于开发环境编译 .scss ...
- mybatis-generator的使用心得
之前开发了一个亚健康测评系统,使用的是SSM框架,里面第一次使用到了mybatis-generator逆向代码生成工具,很方便,省去了基本的增删改查的mapper文件及sql的编写,还能避免错误,这里 ...
- 滑动窗口经典题 leetcode 3. 无重复字符的最长子串
题目 解题思路 题目要求找出给定字符串中不含有重复字符的最长子串的长度.这是一个典型的滑动窗口的题目,可以通过滑动窗口去解答. 滑动窗口 具体操作如下图示:找到一个子串 s[left...right] ...