Jackson多态序列化图文详解
作者:Looveh 发布时间:2022-01-26 19:46:08
场景
做一个消息中心,专门负责发送消息。消息分为几种渠道,包括手机通知(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(不指定默认为@type
,include
为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会多出来一个属性,属性名对应的就是@JsonTypeInfo
的property
的值。虽然不影响使用,但是我看着很不舒服。基于我这种情况可以使用include=EXISTING_PROPERTY
。
来源:https://www.cnblogs.com/lvbok/p/16330663.html


猜你喜欢
- 这里我们给定一个集合strings一、写法1–循环for (int i = 0, len = strings.size(); i <
- 对于因为编程错误而导致的异常,或者是不能期望程序捕获的
- 前言通过深入分析Spring源码,我们知道Spring框架包括大致六大模块, 如Web模块,数据库访问技术模块,面向切面模块,基础设施模块,
- 目录基本查询延迟查询属性类型筛选复合from子句多级排序分组联合查询-join合并-zip()分区(分页)并行linq取消长时间运行的并行l
- 本文实例讲述了C#实现向函数传递不定参数的方法。分享给大家供大家参考。具体实现方法如下:using System;class Min{ pu
- 本文实例为大家分享了Android刷新加载框架的具体代码,供大家参考,具体内容如下1.定义一个接口控制下拉和上拉public interfa
- 从SpringMVC源码解析所用的例子,一个http://localhost:9090/web/hi?name=yang请求调用到下面的地方
- 自定义view的第三篇,模仿的是微博运动界面的个人出生日期设置view,先看看我的效果图: 支持设置初始年份,左右滑动选择出生年份,对应的T
- 项目结构:运行效果:========================================================下面是代
- 1)页面跳转 直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转。 返回带有前缀的字符串:转发:
- 本文实例讲述了Java内置观察者模式。分享给大家供大家参考,具体如下:之前也简单地写过观察者模式(又称为发布-订阅模式)小例子,现在项目中也
- 在项目中引入springcloud中的gateway时报以下错误Description:Parameter 0 of method modi
- 目录一. 已有倒计时方案存在的问题1. CountDownTimer2. Handler3. Timer二. 自己封装倒计时总结一. 已有倒
- 本文实例讲述了C#简单创建和删除目录的方法。分享给大家供大家参考。具体如下:using System;using System.IO;cla
- 这一篇文章介绍SpringBoot应用修改默认打jar形式部署为打war包形式,部署到外部Tomcat。SpringBoot应用默认打包成为
- 使用字典存储事件实例accessor-declarations 的一种用法是公开很多事件但不为每个事件分配字段,而是使用字典来存储这些事件实
- 从Java 5开始,Java语言对方法参数支持一种新写法,叫 可变长度参数列表,其语法就是类型后跟...,表示此处接受的参数为0到多个Obj
- 本文实例为大家分享了Android登录注册功能的具体代码,供大家参考,具体内容如下展示效果代码区MainActivity(登录方法)publ
- 以前我们说过在一些简单的例子中,比如为一个字段赋值或递增该字段,我们需要对线程进行同步,虽然lock可以满足我们的需要,但是一个竞争锁一定会
- 本文通过实例来介绍如何使用commons-fileupload.jar,Apache的commons-fileupload.jar可方便的实