在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事件监 ...
随机推荐
- MegEngine基本概念
MegEngine基本概念 基本概念 MegEngine 是基于计算图的深度神经网络学习框架. 本文内容会简要介绍计算图及其相关基本概念,以及在 MegEngine 中的实现. 计算图 结合一个简单的 ...
- 利用MONAI加速医学影像学的深度学习研究
利用MONAI加速医学影像学的深度学习研究 Accelerating Deep Learning Research in Medical Imaging Using MONAI 医学开放式人工智能网络 ...
- Go探索-String
字符 字符梗概 ASCII字符集 → (GB2312,BIG5,GBK...) → unicode通用字符集 → utf-8 , ascii子符集,一个符号对应一个数字编号,数字编号即可以二进制形式表 ...
- Spring Cloud系列(一):服务注册中心
一.Spring Cloud简介 Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线).分布式系统的协调导致了样 ...
- IDEA骚技巧
1. var 声明 2. null 判空 3. notnull 判非空 4. nn 判非空 5. for 遍历 6. fori 带索引的遍历 7. not 取反 8. if 条件判断 9. cast ...
- FlinkSQL写入Kafka/ES/MySQL示例-JAVA
一.背景说明 Flink的API做了4层的封装,上两层TableAPI.SQL语法相对简单便于编写,面对小需求可以快速上手解决,本文参考官网及部分线上教程编写source端.sink端代码,分别读取s ...
- 理解Spring:IOC的原理及手动实现
Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架.也是几乎所有Java工作者必须要掌握的框架之一,其优秀的设计思想以及其代码实现上的艺术也是我们需要掌握的. ...
- Redis不是只有get set那么简单
我以前还没接触Redis的时候,听到大数据组的小伙伴在讨论Redis,觉得这东西好高端,要是哪天我们组也可以使用下Redis就好了,好长一段时间后,我们项目中终于引入了Redis这个技术,我用了几下, ...
- Mybatis 中经典的 9 种设计模式!面试可以吹牛了
虽然我们都知道有23个设计模式,但是大多停留在概念层面,真实开发中很少遇到.Mybatis源码中使用了大量的设计模式,阅读源码并观察设计模式在其中的应用,能够更深入的理解设计模式. Mybatis至少 ...
- 冷饭新炒 | 深入Quartz核心运行机制
目录 Quartz的核心组件 JobDetail Trigger 为什么JobDetail和Trigger是一对多的关系 常见的Tigger类型 怎么排除掉一些日期不触发 Scheduler List ...