在项目中经常会有多个子类继承一个抽象类或者是实现一个接口,当我们需要对接收到的消息进行反序列化时,就会出现问题,代码如下:
@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中已经存在的属性被包含到序列化的结果中。