Jackson多态序列化怎么实现

这篇文章主要介绍“Jackson多态序列化怎么实现”,在日常操作中,相信很多人在Jackson多态序列化怎么实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Jackson多态序列化怎么实

这篇文章主要介绍“Jackson多态序列化怎么实现”,在日常操作中,相信很多人在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字段丢失。

Jackson多态序列化怎么实现

Jackson多态序列化怎么实现

这与我预想的不符,因为客户端知道渠道,构建对应的渠道消息体给我就好了啊!为什么类型被擦除了呢?我的想法就是发送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;
  }
}

Jackson多态序列化怎么实现

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

Jackson多态序列化怎么实现

Jackson多态序列化怎么实现

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

Jackson多态序列化怎么实现

到此,关于“Jackson多态序列化怎么实现”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注恰卡网网站,小编会继续努力为大家带来更多实用的文章!

本站部分文章来自网络或用户投稿,如无特殊说明或标注,均为本站原创发布。涉及资源下载的,本站旨在共享仅供大家学习与参考,如您想商用请获取官网版权,如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
后端

Spring Cloud中Feign怎么自定义配置与使用

2022-6-22 10:04:43

后端

Golang接口型函数如何使用

2022-6-22 10:04:49

搜索