Jackson多态序列化图文详解

目录
  • 场景
  • Jackson多态类型序列化/反序列化
  • 实战
  • 总结

场景

做一个消息中心,专门负责发送消息。消息分为几种渠道,包括手机通知(Push)、短信(SMS)、邮件(Email),Websocket等渠道。

我定义了一个基类MessageRequest用来接收请求参数,代码如下:

public class MessageRequest implements Serializable {
  protected MessageChannel channel;
  private MessageRequest(){}
  protected MessageRequest(MessageChannel channel){
    this.channel = channel;
  }

  public MessageChannel getChannel() {
    return this.channel;
  }
}

MessageRequest中有个属性channel是枚举MessageChannel,该枚举列举所有渠道,代码如下:

public enum MessageChanne {
  PUSH,
  EMAIL,
  WEBSOCKET,
  SMS,
  ;

  MessageChannel() {}
}

MessageRequest有各种渠道的子类实现,以Push为例:

public class PushMessageReuqest extends MessageRequest {
  public PushMessageRequest() {
    super(MessageChannel.PUSH);
  }

  private String title;
  // 省略其他字段以及getter、setter方法
  ...
}

我在接口入参使用MessageRequest接收:

public class MessageController {
  @PostMapping("/sendMessage")
  public R<Object> sendMessage(MessageRequest request) {
    System.out.println(request);
  }
}

使用postman发送push请求之后发现后端收到的类型还是基类,并且title字段丢失。

这与我预想的不符,因为客户端知道渠道,构建对应的渠道消息体给我就好了啊!为什么类型被擦除了呢?我的想法就是发送push请求啊。。。。。后来才知道序列化之后在反序列化的时候不知道给你反序列化成什么类型,序列化工具也没有聪明到能根据你的channel属性就知道是什么类型,但是我又想这样做。那么怎么办呢????

Jackson多态类型序列化/反序列化

经过查询资料以及咨询了一下领导,发现了@JsonTypeInfo@JsonSubTypes两个注解。

@JsonTypeInfo作用于类/接口,被用来开启多态类型处理,它有一些属性:

  • use(必选):定义使用哪一种类型标识码,有以下几个可选项。

    • NONE:不使用识别码
    • CLASS:使用完全限定类名做识别码
    • MINIMAL_CLASS:使用类名(忽略包名)做识别码,和基类在同一个包可用
    • NAME:指定名称
    • CUSTOM:自定义识别码,由@JsonTypeIdResolver对应
  • include(可选):指定识别码如何被包含进去,有以下几个可选项。
    • PROPERTY:作为兄弟属性加入,默认值
    • WRAPPER_OBJECT:作为一个包装的对象
    • WRAPPER_ARRAY:作为包装的数组
    • EXTERNAL_PROPERTY:作为扩展属性
    • EXISTING_PROPERTY:作为已存在的属性(符合我的场景,用channel)
  • property(可选):指定识别码的属性名称。该属性只有当use为CLASS(不指定默认为@class)、MINIMAL_CLASS(不指定默认为@c)、NAME(不指定默认为@typeinclude为PROPERTY、EXISTING_PROPERTY、EXTERNAL_PROPERTY时才有效。
  • defaultImpl(可选):如果类型识别码不存在或者无效,可以使用该属性来指定反序列化时使用的默认类型。
  • visible(可选,默认false):属性定义了类型标识符是否会成为反序列化器的一部分,默认为false,也就是说Jackson会从json内容中删除类型标识再传递给JsonDeserializer。

@JsonSubTypes作用于类/接口,用来列出给定类/接口的子类。一般配合@JsonTypeInfo使用

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "channel")
@JsonSubTypes({
  @JsonSubTypes.Type(value = PushMessageRequest.class, name = "PUSH"),
  @JsonSubTypes.Type(value = EmailMessageRequest.class, name = "EMAIL")
})

JsonSubTypes的值是一个@JsonSubTypes.Type[]数组,参数value表示类型,参数name表示@JsonTypeInfo注解中property属性的值,对比以上代码即:channel = "PUSH"或channel = "EMAIL"。name为可选值,不指定时需在子类提供JsonTypeName注解并指定value属性。

实战

改造上面提供的MessageReuqest

// include默认为PROPERTY,这里可以不加
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "channel")
@JsonSubTypes({
  @JsonSubTypes.Type(value = PushMessageRequest.class, name = "PUSH"),
  @JsonSubTypes.Type(value = EmailMessageRequest.class, name = "EMAIL")
})
public class MessageRequest implements Serializable {

  protected MessageChannel channel;

  private MessageRequest(){}

  protected MessageRequest(MessageChannel channel){
    this.channel = channel;
  }

  public MessageChannel getChannel() {
    return this.channel;
  }
}

此时通过postman请求发现入参类型有了变化

include属性使用默认的PROPERTY时发现序列化之后的json会多出来一个属性,属性名对应的就是@JsonTypeInfoproperty的值。虽然不影响使用,但是我看着很不舒服。基于我这种情况可以使用include=EXISTING_PROPERTY

总结

到此这篇关于Jackson多态序列化的文章就介绍到这了,更多相关Jackson多态序列化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 使用Jackson反序列化遇到的问题及解决

    Jackson反序列化遇到的问题 最近在项目中需要使用Jackson把前台转来的字符转为对象,转换过程中发生了错误,报错如下 ​com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of java.util.Date from String value '2018-09-14 15:12:08': not a valid representation (error: Failed

  • Jackson序列化和反序列化忽略字段操作

    一.设置Jackson序列化时只包含不为空的字段 new ObjectMapper().setSerializationInclusion(Include.NON_NULL); 二.设置在反序列化时忽略在JSON字符串中存在,而在Java中不存在的属性 new ObjectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 三.Jackson序列化时忽略字段的方式 1.方式一:FilterProvider a)在需

  • Jackson序列化丢失泛型的解决

    Jackson序列化丢失泛型 经过 项目中遇到一个奇怪的bug,即一个Map<Integer,List<Integer>>的泛型map,向map中get一个存在的key,事实上却返回null. 经过排查,发现是该map被Jackson序列化后,key的类型从Integer变成了String类型.再经过反序列化,即使已经声明key泛型的Integer,反序列化后内存数据中的key为String并不是Integer类型且并未抛出异常. 复现 1.声明一个key泛型为Integer的ma

  • jackson序列化和反序列化的应用实践指南

    源码地址:https://github.com/zhouweixin/serializable 1 相关概念 序列化: 把对象转换为字节序列的过程称为对象的序列化 反序列化: 把字节序列恢复为对象的过程称为对象的反序列化 2 序列化的作用 用于把内存中的对象状态保存到一个文件中或者数据库中 用于网络传送对象 用于远程调用传输对象 3 准备序列化对象 准备了两个类, 教师类和学生类, 其中一个学生只有一个教师 这里省略了构造方法和setter, getter方法 Teacher.java publ

  • Jackson多态序列化图文详解

    目录 场景 Jackson多态类型序列化/反序列化 实战 总结 场景 做一个消息中心,专门负责发送消息.消息分为几种渠道,包括手机通知(Push).短信(SMS).邮件(Email),Websocket等渠道. 我定义了一个基类MessageRequest用来接收请求参数,代码如下: public class MessageRequest implements Serializable { protected MessageChannel channel; private MessageRequ

  • 图文详解Java中的序列化机制

    目录 概述 对象序列化和反序列化机制 修改默认的序列化机制 使用transient关键字 自定义readObject.writeObject方法 实现Externalizable接口 serialVersionUID的作用 使用序列化clone 概述 java中的序列化可能大家像我一样都停留在实现Serializable接口上,对于它里面的一些核心机制没有深入了解过.直到最近在项目中踩了一个坑,就是序列化对象添加一个字段以后,使用方系统报了反序列化失败,原因是我们双方的序列化对象没有加上seri

  • idea2020.1 常用设置图文详解

    停用自动更新 停用自动更新,因为我也不知道更新后能不能用... ## 设置作者和代码相关信息在右方写这些东西,下面的description中有相关变量的描述,可以根据情况写 设置版权 点击editor下的加号可以进行版权添加,相关信息就是作者和版权啥的 自动编译 让idea自动编译 设置插件 在setting中的plugins里可以根据自己的需求进行调整,有很多也不咋常用例如Ant啥的 设置字体大小行间距 根据喜好吧 idea卡顿调整 大部分原因是因为给idea分配的内存不够,导致编译或者打代码

  • 使用Spring Boot搭建Java web项目及开发过程图文详解

    一.Spring Boot简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过这种方式,Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者.SpringMVC是非常伟大的框架,开源,发展迅速.优秀的设计必然会划分.解耦.所以,spring有很多子项目,比如core.context.

  • Android 图文详解Binder进程通信底层原理

    之前了解到进程与多进程,涉及多进程不可避免的遇到了进程间通信,说到进程间通信,Binder 成了一道绕不过的坎.接下来咱们逐一了解.

  • Java序列化原理详解

    前言 关于序列化的几种疑问? 什么是序列化?工作中什么时候用到序列化了? 为什么实现了java.io.Serializable接口就能序列化? java中serialVersionUID 为什么不能改变? Serializable序列化和json序列化有什么关系? 你都会哪几种对象深拷贝方式? 以上抛出了几个问题,大家都能回答上来吗?回答不上来的话就接着往下看吧. 前提知识: 讲解之前先扩充一些前提知识. 二进制协议和文本协议 首先我们要知道所有的数据在底层的传输都是二进制流,这点是毋庸置疑的.

  • Mac OS系统下mysql 5.7.20安装教程图文详解

    Mac OS X 下 TAR.GZ 方式安装 MySQL 5.7 与 MySQL 5.6 相比, 5.7 版本在安装时有两处不同: 1:初始化方式改变, 从scripts/mysql_install_db --user=mysql初始化方式变成了bin/mysqld --initialize --user=mysql方式; 2: 初始密码生成改变, 5.6 的版本在 tar gz 方式初始化完成后默认 root 密码为空, 5.7 版本在初始化完成后会生成一个临时的 root 密码: 一.在浏览

  • Microsoft Sql server2005的安装步骤图文详解及常见问题解决方案

    一:安装sql server 2005过程中出现如下问题:"选择的功能中没有任何功能可以安装或升级": 解决方案:Microsoft SQL Server 2005→配置工具→SQL配置管理器→SQL Server 2005服务→右边的两个服务启动SQL Server FullTest Search() 和服务SQl Sever(计算机名) 二:无法将数CLSID写入\Software\Classes\PROTOCOLS\Handler\ms-help. 解决办法:退出电脑安全软件 三

  • vmware 实现linux目录映射window本地目录图文详解

    ---恢复内容开始--- 背景: 1,使用lnmp环境 2,代码可以在windows上面写,直接映射到linux的lnmp环境下面 第一步: vmware 新建一个linux虚拟机 一路下一步到完成 第二步: 安装镜像(自己去下载一个linux的镜像) 然后确认 ,然后重新客户机 然后一路next(语言可以选中文) 直到这一步 给linux设置密码 然后一路next,确认所有修改 第三步: 进入linux系统配置网络 修改下图路径中的文档(onboot改成yes) 修改完保存 重启网络 第四步:

  • redhat7.1 安装mysql 5.7.10步骤详解(图文详解)

    在redhat下安装MySQL,步骤如下 Mysql目录安装位置:/usr/local/mysql 数据库保存位置:/data/mysql 日志保存位置:/data/log/mysql 下载安装包 http://downloads.mysql.com/archives/community/ 1. 获取mysql安装包,mysql-5.7.10-Linux-glibc2.5-x86_64.tar解压后目录如下. 2. 解压mysql-5.7.10-linux-glibc2.5-x86_64.tar

随机推荐