在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事件监 ...
随机推荐
- BERT模型的OneFlow实现
BERT模型的OneFlow实现 模型概述 BERT(Bidirectional Encoder Representations from Transformers)是NLP领域的一种预训练模型.本案 ...
- 浪潮 ClusterEngineV4.0 任意命令执行
1.浪潮ClusterEngineV4.0 任意命令执行 影响版本 ClusterEngineV4.0 2.漏洞影响 远程代码执行 3.复现 fofa语句 title='TSCEV4.0' 抓包构造e ...
- 「题解」agc031_c Differ by 1 Bit
本文将同步发布于: 洛谷博客: csdn: 博客园: 简书: 题目 题目链接:洛谷 AT4693.AtCoder agc031_c. 题意概述 给定三个数 \(n,a,b\),求一个 \(0\sim ...
- 数据结构与算法Python版 熟悉哈希表,了解Python字典底层实现
Hash Table 散列表(hash table)也被称为哈希表,它是一种根据键(key)来存储值(value)的特殊线性结构. 常用于迅速的无序单点查找,其查找速度可达到常数级别的O(1). 散列 ...
- GoLang:通过url将值从view层(.tpl)传递到controller层
beego框架 1.定义路由: beego.Router("/UpdateState/:statename/:id", &controllers.ContentContro ...
- JAVA并行程序基础一
JAVA并行程序基础一 线程的状态 初始线程:线程的基本操作 1. 新建线程 新建线程只需要使用new关键字创建一个线程对象,并且用start() ,线程start()之后会执行run()方法 不要直 ...
- npm i安装命令中的-g -D -S的区别
-g为全局安装 -D 对模块进行局部安装,模块写入到 devDependencies 对象 用于开发阶段,开发时用到的工具等 -S 局部安装,不同的是模块写入到 dependencies对象 用于生产 ...
- ShardingSphere 看这一篇就够了
1.什么是shardingSphere Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 JDBC.Proxy 和 Sidecar(规划中)这 ...
- 创建Akamai CDN
背景说明: XX全球版项目CDN, 原有改之前:主CDN为Akamai,备CDN为Cloudflare. 计划改之后:主CDN为Cloudflare,备CDN为Akamai. 原因:Akamai CD ...
- 29、vi和vim用法详解
vi类似于windows中的文本文件,用于普通的文本文件 vim:专家版的文件编辑器,用于shell程序型文件,带颜色,自检查语法 一般模式快捷键 O:光标到一行的首 $:光标到一行的尾 H:光标到整 ...