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