藉助Jackson的JsonTypeInfo註解實現多態類的解析
一、問題背景
Jackson框架對json欄位的序列化和反序列化默認策略是根據getter和setter方法,去掉get和set,再把首字母小寫,便找到了對應的欄位。通常情況,我們都是對普通的POJO進行serialization/deserialization。那麼如果遇到了解析抽象類(或者介面)呢?如何定位到對應的實現類?實現類都找不到,談何匹配到對應的欄位反序列化。
二、JsonTypeInfo 註解簡單介紹
作用於類或介面,被用來處理多態類型的序列化及反序列化。
This is necessarily for polymorphic types, and may also be needed to link abstract declared types and matching concrete implementation.
三、demo
先寫個小demo對這個功能有個初步的感性認識。
1.抽象類
package jackson;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
@JsonSubTypes({@JsonSubTypes.Type(value = InputPageModel.class, name = "input")
, @JsonSubTypes.Type(value = NumberPageModel.class, name = "number")})
public abstract class PageModel {
private String type;
private String name;
private String uiType;
private String label;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
註解里的visible欄位:如果為false,那麼反序列化時,類型id欄位(在這個demo里是type欄位)的值將不會被反序列化到POJO中。
2.1 實現類1
package jackson;
import lombok.Data;
@Data
public class InputPageModel extends PageModel {
private String input;
}
1
2
3
4
5
6
7
8
9
10
2.2 實現類2
package jackson;
import lombok.Data;
@Data
public class NumberPageModel extends PageModel {
private Integer number;
}
1
2
3
4
5
6
7
8
9
10
3.測試類
package jackson;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class JsonTypeInfoTest {
public static void main(String[] args) {
String inputJson = " {
" +
" "type": "input",
" +
" "label": "標題",
" +
" "uiType": "input",
" +
" "input" : "lvsheng"
" +
"
" +
" }";
ObjectMapper mapper = new ObjectMapper();
try {
InputPageModel inputPageModel = ((InputPageModel) mapper.readValue(inputJson, PageModel.class));
System.out.println(inputPageModel.getInput());
} catch (IOException e) {
e.printStackTrace();
}
String numberJson = " {
" +
" "type": "number",
" +
" "label": "價格",
" +
" "uiType": "input",
" +
" "number" : 110
" +
"
" +
" }";
try {
NumberPageModel numberPageModel = ((NumberPageModel) mapper.readValue(numberJson, PageModel.class));
System.out.println(numberPageModel.getNumber());
} catch (IOException e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
測試類輸出
lvsheng
110
1
2
四、 在大型工程里的應用
這個demo里JsonTypeInfo藉助JsonSubTypes註解來感知抽象類的有哪些實現類,並且是如何匹配的。在大型工程中抽象類的子類很多(介面的實現很多),那麼JsonSubTypes註解就十分臃腫了。而且這種寫法是 違反開閉原則(OCP) 的。藉助以下方式可以將JsonSubTypes剔除掉,達到相同的效果。
1. 給子類加JsonTypeName註解
package jackson;
import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Data;
@Data
@JsonTypeName(value = "input")
public class InputPageModel extends PageModel {
private String input;
}
1
2
3
4
5
6
7
8
9
10
11
12
2. 藉助reflections框架,將所有JsonTypeName註解類掃描出來
ObjectMapper並不具備掃描實現JsonTypeName註解的類,因此需要自己手工掃描所有帶有這個註解的類。
Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(JsonTypeName.class);
1
3. 手工將掃出來的類註冊到ObjectMapper對象
最後一步,完成子類註冊
ObjectMapper mapper = new ObjectMapper();
classSet.parallelStream().forEach(clazz -> mapper.registerSubtypes(clazz));
1
2
這樣便可以在大型工程里優雅的使用jackson解析多態類,每增加一個子類型,無需修改額外的代碼。對擴展開放,對修改封閉。
五、其他主流json框架對多態的支持
框架是否支持備註gson支持不支持註解,使用不方便 官方demofastjson不支持json-lib不支持
當遇到json框架技術選型時,如果有處理多態的需求,那麼jackson無疑是最佳的選擇。
※Swoole實現基於WebSocket的群聊私聊
※Discuz全版本任意文件刪除漏洞
TAG:程序員小新人學習 |