在項目中經常會有多個子類繼承一個抽象類或者是實現一個接口,當我們需要對接收到的消息進行反序列化時,就會出現問題,代碼如下:
@Getter @Setter public abstract class MessageContent { private String contentType; /** * 消息內容(contentText)編碼方式。 * 默認為utf8字符編碼,可選base64編碼。 */ private String contentEncoding; }
它有多個子類,分別為:
@Getter @Setter public class MessageContentBack extends MessageContent { /** * contentType為"text/plain" 內容為字符串,其余的類型 內容為json對象。 * 如果編碼式為base64,則內容為base64編碼之后的字符 */ private String contentText; }
以及:
@Getter @Setter public class ContentTextSuggestionChipList extends MessageContent { private Suggestion contentText; }
異常:
Can not construct instance of XXXX, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
下面我們來講講解決多態中反序列化問題的解決---jackson。
首先,先來看看幾個重要注解以及它們的參數:
@JsonTypeInfo
use:定義使用哪一種類型識別碼,它有下面幾個可選值: 1、JsonTypeInfo.Id.CLASS:使用完全限定類名做識別 2、JsonTypeInfo.Id.MINIMAL_CLASS:若基類和子類在同一包類,使用類名(忽略包名)作為識別碼 3、JsonTypeInfo.Id.NAME:一個合乎邏輯的指定名稱 4、JsonTypeInfo.Id.CUSTOM:自定義識別碼 5、JsonTypeInfo.Id.NONE:不使用識別碼 include(可選):指定識別碼是如何被包含進去的,它有下面幾個可選值: 1、JsonTypeInfo.As.PROPERTY:作為數據的兄弟屬性 2、JsonTypeInfo.As.EXISTING_PROPERTY:作為POJO中已經存在的屬性 3、JsonTypeInfo.As.EXTERNAL_PROPERTY:作為擴展屬性 4、JsonTypeInfo.As.WRAPPER_OBJECT:作為一個包裝的對象 5、JsonTypeInfo.As.WRAPPER_ARRAY:作為一個包裝的數組 property(可選):制定識別碼的屬性名稱 此屬性只有當use為 JsonTypeInfo.Id.CLASS(若不指定property則默認為@class) JsonTypeInfo.Id.MINIMAL_CLASS(若不指定property則默認為@c) JsonTypeInfo.Id.NAME(若不指定property默認為@type) include為JsonTypeInfo.As.PROPERTY、JsonTypeInfo.As.EXISTING_PROPERTY、JsonTypeInfo.As.EXTERNAL_PROPERTY時才有效 defaultImpl(可選):如果類型識別碼不存在或者無效,可以使用該屬性來制定反序列化時使用的默認類型 visible(可選,默認為false):propery中的屬性是否反序列化到POJO中
屬性定義了類型標識符的值是否會通過JSON流成為反序列化器的一部分,默認為fale,也就是說,jackson會從JSON內容中處理和刪除類型標識符再傳遞給JsonDeserializer
@JsonSubtypes
作用於類/接口,用來列出給定類的子類,只有當子類類型無法被檢測到時才會使用它
一般是配合@JsonTypeInfo在基類上使用,比如:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,include = JsonTypeInfo.As.EXISTING_PROPERTY ,property = "contentType", visible = true) @JsonSubTypes({ @JsonSubTypes.Type(value = MessageContentBack.class, name = "text/plain"), @JsonSubTypes.Type(value = MessageContentFile.class, name = "application/vnd.gsma.rcs-ft-http"), @JsonSubTypes.Type(value = MessageContentReceiveResponse.class, name = "application/vnd.gsma.botsuggestion.response.v1.0+json"), @JsonSubTypes.Type(value = MessageContentSharedData.class, name = "application/vnd.gsma.botsharedclientdata.v1.0+json"), })
特別說明:
剛開始在寫代碼時,由於不熟悉jackson,include屬性選擇的是JsonTypeInfo.As.PROPERTY,發現在對它的子類進行序列化的時候,property屬性中的內容(這里是“contentType”)作為兄弟屬性被序列化了一次,即序列化后的結果中出現兩個相同的屬性“contentType”。后來再查閱資料發現JsonTypeInfo.As.PROPERTY的意思是property中的屬性作為數據的兄弟屬性會被序列化一次,而JsonTypeInfo.As.EXITING_PROPERTY則是作為POJO中已經存在的屬性被包含到序列化的結果中。