记录在项目中遇到的一个多态序列化的问题
场景:
需要创建多个事件(click、MouseIn...),事件可能有多种不同的响应类型(弹窗、页面跳转)。
实体如下:
/**
* 事件对象
*/
@Data
public class Event {
/**
* 事件编码
*/
private String code;
/**
* 事件类型
*/
private String eventType;
/**
* 事件响应
*/
private EventResponse response;
}
/**
* 响应对象的
*/
@Data
public class EventResponse {
}
/**
* 页面跳转响应对象
*/
@Data
public class PageTurns extends EventResponse{
/**
* 响应类型为page
*/
private final String responseType = "page";
/**
* 跳转页面的url
*/
private String url;
}
/**
* 弹窗响应对象
*/
@Data
public class PopupContent extends EventResponse{
/**
* 响应类型为popup
*/
private final String responseType = "popup";
/**
* 弹窗内容
*/
private String content;
}
接收的VO是Event,可以看到当响应类型不同时接收的参数不同,创建的对象类型也不同。
也就是希望能在序列化参数时自动帮我们序列化为子类对象,这也就是我提到多态序列化问题。
解决
@JsonTypeInfo注解作用于类/接口可以开启多态类型处理
该注解有如下几个属性
-
use:定义使用哪一种类型识别码
-
include(可选):设置识别码包含在哪里。
-
property(可选):识别码的名称,根据 use的属性值不同,默认值不同
-
visible(可选): 定义识别码在反序列化时是否保留(不管false或true都不影响序列化)。默认是false,表示Jackson可以将识别码识别为类型后就删除
@JsonSubTypes 配置可以匹配的子类型
我们这里选择的
- use: JsonTypeInfo.Id.NAME 按照JsonTypeInfo的值作为匹配码来匹配
- include:JsonTypeInfo.As.EXTERNAL_PROPERTY 匹配码是一个与类属性同级的额外属性
- property:responseType
- visible :true
/**
* 事件对象
*/
@Data
public class Event {
/**
* 事件编码
*/
private String code;
/**
* 事件类型
*/
private String eventType;
private String responseType;
/**
* 事件响应
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, //按照JsonTypeInfo的值作为匹配码
include = JsonTypeInfo.As.EXTERNAL_PROPERTY, //与类属性同级的额外属性
property = "responseType") //额外属性的属性名为 responseType
@JsonSubTypes({
@JsonSubTypes.Type(value = PageTurns.class, name = "page"),
@JsonSubTypes.Type(value = PopupContent.class, name = "popup"),
})
private EventResponse response;
}
/**
* 响应对象
*/
@Data
public class EventResponse {
}
/**
* 页面跳转响应对象
*/
@Data
public class PageTurns extends EventResponse{
private final String responseType = "page" ;
/**
* 跳转页面的url
*/
private String url;
}
/**
* 弹窗响应对象
*/
@Data
public class PopupContent extends EventResponse{
private final String responseType = "popup" ;
/**
* 弹窗内容
*/
private String content;
}
测试
// 通过controller接收 Event
@PostMapping("/multilstate/serialize")
public Result<Event> createEvent(@RequestBody Event event){
if (event.getResponse() instanceof PopupContent){
System.out.println("创建了一个响应类型为 PopupContent 的事件 event: "+event);
}else if ( event.getResponse() instanceof PageTurns){
System.out.println("创建了一个响应类型为 PageTurns 的事件 event: "+event);
}
return Result.success(event);
}
// 请求参数1
{
"code":"001",
"eventType":"click",
"responseType":"page",
"response":{
"url":"https://www.baidu.com"
}
}
/*
控制台输出
创建了一个响应类型为 PageTurns 的事件 event: Event(code=001, eventType=click, responseType=page, response=PageTurns(responseType=page, url=https://www.baidu.com))
*/