场景

做一个消息中心,专门负责发送消息。消息分为几种渠道,包括手机通知(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多态序列化的更多相关文章

  1. jackson json序列化 首字母大写 第二个字母需小写

    有这样一个类: @Setter @Getter @JsonNaming(value = PropertyNamingStrategy.UpperCamelCaseStrategy.class) pub ...

  2. JSON类库Jackson优雅序列化Java枚举类

    1. 前言 在Java开发中我们为了避免过多的魔法值,使用枚举类来封装一些静态的状态代码.但是在将这些枚举的意思正确而全面的返回给前端却并不是那么顺利,我们通常会使用Jackson类库序列化对象为JS ...

  3. 【Springboot】FastJson与Jackson全局序列化方式的配置和相关工具类

    springboot 版本: <parent> <groupId>org.springframework.boot</groupId> <artifactId ...

  4. 20151024_004_C#基础知识(C#中的访问修饰符,继承,new关键字,里氏转换,is 和 as,多态,序列化与反序列化)

    1:C#中的访问修饰符 public: 公共成员,完全公开,没有访问限制. private: 私有的,只能在当前类的内部访问. protected: 受保护的,只能在当前类的内部以及该类的子类中访问. ...

  5. spring boot 是如何利用jackson进行序列化的?

    接上一篇:spring boot 是如何利用jackson进行反序列化的? @RestController public class HelloController { @RequestMapping ...

  6. json多态序列化

    https://blog.csdn.net/java_huashan/article/details/46428971 https://blog.csdn.net/bruce128/article/d ...

  7. jackson对多态or多子类序列化的处理配置

    [TOC] Jackson Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json.xml转换成Java对象. 多态类型的处理 jackson允许配置多态类型处理, ...

  8. Jackson对多态和多子类序列化的处理配置

    目录 Jackson 多态类型的处理 Jackson Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json.xml转换成Java对象. 多态类型的处理 jacks ...

  9. Jackson 序列化和反序列化

    博客地址:https://www.moonxy.com 一.前言 Jackson 功能很强大,既能满足简单的序列化和反序列化操作,也能实现复杂的.个性化的序列化和反序列化操作.到目前为止,Jackso ...

随机推荐

  1. 【Android开发】Coding + git命令行基本使用

    上传代码 进入本地仓库的目录. cd ... 查看仓库链接 : git remote -v 如果没有,则添加url链接 : git remote add testName https://git.co ...

  2. Java在方法中定义可变参数类型

    学习目标: 掌握可变参数的应用 学习内容: 1.定义 在方法中传递数组有一种更简单的方式--方法的可变参数,其本质是一个语法糖,目的是让开发者写代码更简单. 2.语法 [修饰符] 返回值类型 方法名称 ...

  3. Exchange 2013 中 NDR 常见的失败返回状态代码

    增强状态代码 描述 可能的原因 其他信息 4.3.1 Insufficient system resources 发生内存不足错误.资源问题(例如磁盘已满)可能导致该问题.您可能会收到内存不足错误,而 ...

  4. [源码解析] TensorFlow 分布式环境(8) --- 通信机制

    [源码解析] TensorFlow 分布式环境(8) --- 通信机制 目录 [源码解析] TensorFlow 分布式环境(8) --- 通信机制 1. 机制 1.1 消息标识符 1.1.1 定义 ...

  5. spring原始注解开发-01

    我们使用xml-Bean标签的配置方式和注解做对比理解 1.创建UserDao接口以及UserDao的实现类UserDaoImpl(接口代码省略) public class UserDaoImpl i ...

  6. kubectl scale sts

    使用scale 不单单是扩容还可以:1.动态扩展服务,增加承载能力2.如果出现pod异常,可以利用这种方式,增加pod,再删除原来的pod 比如:pod所在宿主机网络或者宿主机死掉注: 但是一旦有某个 ...

  7. 帝国cms一键排版删除段落前两个空格和换行符

    打开网站根目录下e\class\function.php文件:一.删除两个空格: 搜索:$nbsp=' '; 改为:$nbsp=''; 二.删除<br>换行符 搜索: $ok='<b ...

  8. GIL全局解释器锁、协程运用、IO模型

    GIL全局解释器锁 一.什么是GIL 首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念.就好比C是一套语言(语法)标准,但是可以用不 ...

  9. vue 组件复用 - component

    vue 组件复用 - component vue 组件复用 就是对 component 标签的使用 先看图 下图看使用 结果: 可以看到 在箱包 这一项,我将banner 组件用了两次,我 每次 点击 ...

  10. el-tree小知识点

    <el-tree ref="tree" :props="props" :data="initData" node-key=" ...