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多态的(反)序列化的更多相关文章

  1. Spring学习---Spring中利用jackson进行JSON转换

    Spring中利用jackson进行JSON转换 import java.util.List; import com.fasterxml.jackson.core.JsonProcessingExce ...

  2. Java下利用Jackson进行JSON解析和序列化

    Java下利用Jackson进行JSON解析和序列化   Java下常见的Json类库有Gson.JSON-lib和Jackson等,Jackson相对来说比较高效,在项目中主要使用Jackson进行 ...

  3. 【转载】Java下利用Jackson进行JSON解析和序列化

    参考资料: https://blog.csdn.net/sdut406/article/details/85647982 Java下常见的Json类库有Gson.JSON-lib和Jackson等,J ...

  4. Java下利用Jackson进行JSON解析和序列化1

    Java下常见的Json类库有Gson.JSON-lib和Jackson等,Jackson相对来说比较高效,在项目中主要使用Jackson进行JSON和Java对象转换,下面给出一些Jackson的J ...

  5. 剑指Offer——企业级项目中分层的含义与依据及多态的优势

    剑指Offer--企业级项目中分层的含义与依据及多态的优势   关于以上两点,由于项目经验较少,自己不是很明白,特整理如下. 常见分层架构模式 三层架构 3-tier architecture   微 ...

  6. Spring-boot 项目中使用 jackson 遇到的一个问题

    jackson介绍 java代码中实现序列化和反序列化的工具类 jackson使用Demo https://github.com/Naylor55/JavaDebrisCode/tree/branch ...

  7. 如何在我们项目中利用开源的图表(js chart)

            最近觉得应该把自己在技术上的一些心得记录在博客里面跟大家分享,一起讨论,一起成长!       这篇随笔主要为介绍chart在项目中的运用,因为在我们看到一些开源的chart时候,是使 ...

  8. 在项目中利用TX Text Control进行WORD文档的编辑显示处理

    在很多文档管理的功能模块里面,我们往往需要对WORD稳定进行展示.编辑等处理,而如果使用微软word控件进行处理,需要安装WORD组件,而且接口使用也不见得简单易用,因此如果有第三方且不用安装Offi ...

  9. vue项目中利用popstate处理页面返回操作

    需求背景:项目中需要做一个返回确认,避免用户误触返回键而退出当前页面. 原理:利用history和浏览器刷新popstate状态 实现: 1.在mounted() 阶段判断并添加popstate事件监 ...

随机推荐

  1. GPU 硬件虚拟化Hardware Virtualization

    GPU 硬件虚拟化Hardware Virtualization 1. Principles 一个物理GPU可以虚拟化为多个vGPUs.VMs可以绑定到vGPUs以直接访问一些物理GPU资源. 2. ...

  2. CUDA功能和通用功能

    CUDA功能和通用功能 本文描述了类似于CUDA ufunc的对象. 为了支持CUDA程序的编程模式,CUDA Vectorize和GUVectorize无法产生常规的ufunc.而是返回类似ufun ...

  3. 用Microsoft DirectX光线跟踪改善渲染质量

    用Microsoft DirectX光线跟踪改善渲染质量 Implementing Stochastic Levels of Detail with Microsoft DirectX Raytrac ...

  4. Docker Context基本原理

    Docker Context基本原理 介绍 本指南介绍了上下文如何使单个Docker CLI轻松管理多个Swarm集群.多个Kubernetes集群和多个单独的Docker节点. 单个Docker C ...

  5. 电阻存储器为edge-AI提供了仿生架构

    电阻存储器为edge-AI提供了仿生架构 Resistive memories enable bio-inspired architectures for edge AI 近年来,脑启发计算领域的研究 ...

  6. CAP 超详细名词解释

    目录 引言 概述 分布式 一致性 ACID中的一致性 可用性 分区容错性 可用性与分区容错性,傻傻分不清 问题1:分区容错性说分区故障正常工作,什么叫正常工作?这个正常工作是指满足可用性吗? 问题2: ...

  7. SQL进阶总结(一)

    学而不思则罔,趁着假期好好总结下SQL的编程知识. 掌握SQL,首先有两个知识点要明确,要贯穿在我们整个学习SQL的过程中. SQL 不同于之前学习的面向过程.面向编程语言,SQL是一门面向集合的编程 ...

  8. spring 声明式事务剖析

    spring事务是在数据库事务的基础上进行封装扩展, 支持原有事务的隔离级别, 加入了事务传播的概念,提供多个事务合并和分割的功能, 提供声明式事务,让事务和业务代码分开 spring提供了三个接口供 ...

  9. some requirement checks failed

    1.执行安装数据库软件时报错(./runInstaller): 解决:(1)su - root       执行: x host+    然后 su - oracle    执行:./runIstal ...

  10. beego搭建api服务

    beego介绍 beego是一个Golang实现的开源Go应用开发框架,他可以用来快速开发 API.Web 及后端服务等各种应用,是一个 RESTful的框架,主要设计灵感来源于tornado.sin ...