當前位置:
首頁 > 知識 > 藉助Jackson的JsonTypeInfo註解實現多態類的解析

藉助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無疑是最佳的選擇。

藉助Jackson的JsonTypeInfo註解實現多態類的解析

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

Swoole實現基於WebSocket的群聊私聊
Discuz全版本任意文件刪除漏洞

TAG:程序員小新人學習 |