在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事件监 ...
随机推荐
- YOLOv4:目标检测(windows和Linux下Darknet 版本)实施
YOLOv4:目标检测(windows和Linux下Darknet 版本)实施 YOLOv4 - Neural Networks for Object Detection (Windows and L ...
- GStreamer 1.0 series序列示例
GStreamer 1.0 series序列示例 OpenEmbedded layer for GStreamer 1.0 这layer层为GStreamer 1.0框架提供了非官方的支持,用于Ope ...
- 使用js获取checkbox控件在GridView中的第几行
这次的知识点是如何使用js获取checkbox控件所在的是第几行!!! 我们可以使用 JavaScript 中自带的 rowIndex 和 cellIndex 来获取行和列的键值 (从0开始) 这两个 ...
- 记 Ant Designer Vue 2.0.1 layout 丢失样式类名问题分析
现象 <a-layout-sider /> 渲染到页面上会变成 <section class="undefined-has-sider"> 丢失了 layo ...
- springcloud中 getway中的断言配置: Predicate 9中配置过程, getway的 filters实现限流功能:
https://www.cnblogs.com/grasp/p/11506426.html 这里引用别人的,,且试验过 ,没问题 server: port: 9527 spring: applicat ...
- LeetCode 每日一题「判定字符是否唯一」
我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. 题目 ...
- Headline 项目总结中
目录 1.项目准备 1.1 rem适配 1.2 通用样式CSS 1.3删除测试代码 1.4Git托管 2.login页面 2.1 页面布局和表单校验 2.2login页的接口抽取 2.5.loadin ...
- 十、使用Varnish加速Web
使用Varnish加速Web 构建Web服务器(web1) [root@web1 ~]# yum -y install httpd [root@web1 ~]# systemctl start ...
- 【NX二次开发】打开信息窗口UF_UI_open_listing_window
头文件:uf_ui_ugopen.h函数名:UF_UI_open_listing_window 函数说明:打开信息窗口 测试代码: #include <uf.h> #include < ...
- Spring Cloud Gateway简单入门,强大的微服务网关
我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 简介 见名知义,Spring Cloud Gateway是用于微服务场景的网关组件,它是基于Spring WebFlu ...