Jackson多態(tài)序列化圖文詳解
場(chǎng)景
做一個(gè)消息中心,專(zhuān)門(mén)負(fù)責(zé)發(fā)送消息。消息分為幾種渠道,包括手機(jī)通知(Push)、短信(SMS)、郵件(Email),Websocket等渠道。
我定義了一個(gè)基類(lèi)MessageRequest用來(lái)接收請(qǐng)求參數(shù),代碼如下:
public class MessageRequest implements Serializable {
protected MessageChannel channel;
private MessageRequest(){}
protected MessageRequest(MessageChannel channel){
this.channel = channel;
}
public MessageChannel getChannel() {
return this.channel;
}
}在MessageRequest中有個(gè)屬性channel是枚舉MessageChannel,該枚舉列舉所有渠道,代碼如下:
public enum MessageChanne {
PUSH,
EMAIL,
WEBSOCKET,
SMS,
;
MessageChannel() {}
}MessageRequest有各種渠道的子類(lèi)實(shí)現(xiàn),以Push為例:
public class PushMessageReuqest extends MessageRequest {
public PushMessageRequest() {
super(MessageChannel.PUSH);
}
private String title;
// 省略其他字段以及getter、setter方法
...
}我在接口入?yún)⑹褂?code>MessageRequest接收:
public class MessageController {
@PostMapping("/sendMessage")
public R<Object> sendMessage(MessageRequest request) {
System.out.println(request);
}
}使用postman發(fā)送push請(qǐng)求之后發(fā)現(xiàn)后端收到的類(lèi)型還是基類(lèi),并且title字段丟失。


這與我預(yù)想的不符,因?yàn)榭蛻?hù)端知道渠道,構(gòu)建對(duì)應(yīng)的渠道消息體給我就好了啊!為什么類(lèi)型被擦除了呢?我的想法就是發(fā)送push請(qǐng)求啊。。。。。后來(lái)才知道序列化之后在反序列化的時(shí)候不知道給你反序列化成什么類(lèi)型,序列化工具也沒(méi)有聰明到能根據(jù)你的channel屬性就知道是什么類(lèi)型,但是我又想這樣做。那么怎么辦呢????
Jackson多態(tài)類(lèi)型序列化/反序列化
經(jīng)過(guò)查詢(xún)資料以及咨詢(xún)了一下領(lǐng)導(dǎo),發(fā)現(xiàn)了@JsonTypeInfo和@JsonSubTypes兩個(gè)注解。
@JsonTypeInfo作用于類(lèi)/接口,被用來(lái)開(kāi)啟多態(tài)類(lèi)型處理,它有一些屬性:
- use(必選):定義使用哪一種類(lèi)型標(biāo)識(shí)碼,有以下幾個(gè)可選項(xiàng)。
NONE:不使用識(shí)別碼CLASS:使用完全限定類(lèi)名做識(shí)別碼MINIMAL_CLASS:使用類(lèi)名(忽略包名)做識(shí)別碼,和基類(lèi)在同一個(gè)包可用NAME:指定名稱(chēng)CUSTOM:自定義識(shí)別碼,由@JsonTypeIdResolver對(duì)應(yīng)
- include(可選):指定識(shí)別碼如何被包含進(jìn)去,有以下幾個(gè)可選項(xiàng)。
PROPERTY:作為兄弟屬性加入,默認(rèn)值WRAPPER_OBJECT:作為一個(gè)包裝的對(duì)象WRAPPER_ARRAY:作為包裝的數(shù)組EXTERNAL_PROPERTY:作為擴(kuò)展屬性EXISTING_PROPERTY:作為已存在的屬性(符合我的場(chǎng)景,用channel)
- property(可選):指定識(shí)別碼的屬性名稱(chēng)。該屬性只有當(dāng)
use為CLASS(不指定默認(rèn)為@class)、MINIMAL_CLASS(不指定默認(rèn)為@c)、NAME(不指定默認(rèn)為@type,include為PROPERTY、EXISTING_PROPERTY、EXTERNAL_PROPERTY時(shí)才有效。 - defaultImpl(可選):如果類(lèi)型識(shí)別碼不存在或者無(wú)效,可以使用該屬性來(lái)指定反序列化時(shí)使用的默認(rèn)類(lèi)型。
- visible(可選,默認(rèn)false):屬性定義了類(lèi)型標(biāo)識(shí)符是否會(huì)成為反序列化器的一部分,默認(rèn)為false,也就是說(shuō)Jackson會(huì)從json內(nèi)容中刪除類(lèi)型標(biāo)識(shí)再傳遞給JsonDeserializer。
@JsonSubTypes作用于類(lèi)/接口,用來(lái)列出給定類(lèi)/接口的子類(lèi)。一般配合@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的值是一個(gè)@JsonSubTypes.Type[]數(shù)組,參數(shù)value表示類(lèi)型,參數(shù)name表示@JsonTypeInfo注解中property屬性的值,對(duì)比以上代碼即:channel = "PUSH"或channel = "EMAIL"。name為可選值,不指定時(shí)需在子類(lèi)提供JsonTypeName注解并指定value屬性。
實(shí)戰(zhàn)
改造上面提供的MessageReuqest
// include默認(rèn)為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;
}
}
此時(shí)通過(guò)postman請(qǐng)求發(fā)現(xiàn)入?yún)㈩?lèi)型有了變化


include屬性使用默認(rèn)的PROPERTY時(shí)發(fā)現(xiàn)序列化之后的json會(huì)多出來(lái)一個(gè)屬性,屬性名對(duì)應(yīng)的就是@JsonTypeInfo的property的值。雖然不影響使用,但是我看著很不舒服。基于我這種情況可以使用include=EXISTING_PROPERTY。

總結(jié)
到此這篇關(guān)于Jackson多態(tài)序列化的文章就介紹到這了,更多相關(guān)Jackson多態(tài)序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java線(xiàn)程間通信不同步問(wèn)題原理與模擬實(shí)例
這篇文章主要介紹了Java線(xiàn)程間通信不同步問(wèn)題,結(jié)合實(shí)例形式分析了java線(xiàn)程間通信不同步問(wèn)題的原理并模擬實(shí)現(xiàn)了線(xiàn)程間通信不同步情況下的異常輸出,需要的朋友可以參考下2019-10-10
springboot全局字符編碼設(shè)置解決亂碼問(wèn)題
這篇文章主要介紹了springboot全局字符編碼設(shè)置解決亂碼問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
java中break和continue區(qū)別及使用場(chǎng)合分析
本文力圖通過(guò)實(shí)例加使用場(chǎng)合詳解來(lái)引導(dǎo)菜鳥(niǎo)重新認(rèn)識(shí)break和continue語(yǔ)句,需要的朋友可以參考下2014-01-01
Mac系統(tǒng)搭建JDK及JMETER過(guò)程解析
這篇文章主要介紹了Mac系統(tǒng)搭建JDK及JMETER過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
Java實(shí)現(xiàn)計(jì)算機(jī)程序設(shè)計(jì)思路
這篇文章主要為大家介紹了Java實(shí)現(xiàn)計(jì)算機(jī)程序設(shè)計(jì)思路,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
Spring Security中用JWT退出登錄時(shí)遇到的坑
使用了JWT后,每次請(qǐng)求都要攜帶 Bearer Token 并且被專(zhuān)門(mén)的過(guò)濾器攔截解析之后才能將用戶(hù)認(rèn)證信息保存到 SecurityContext 中去,接下來(lái)通過(guò)本文給大家介紹Spring Security中用JWT退出登錄時(shí)遇到的坑,感興趣的朋友一起看看吧2021-10-10
Java中MapStruct復(fù)制對(duì)象的具體使用
MapStruct是一個(gè)用于Java的代碼生成器,可以自動(dòng)生成類(lèi)型安全的Bean映射代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
Springboot mybatisplus如何解決分頁(yè)組件IPage失效問(wèn)題
這篇文章主要介紹了Springboot mybatisplus如何解決分頁(yè)組件IPage失效問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08

