fastJson反序列化處理泛型 我能從中學到什麼
都會的json解析
在我們日常的編碼工作中,常常會制定或者遇到這樣的json結構
{
"resultcode": "200","reason": "成功的返回","result": {
"area": "浙江省溫州市平陽縣","sex": "男","birthday": "1989年03月08日"
}
}
複製程式碼
對於該介面的提供者,最外層的resultcode
reason
和result
這三個元素都是有的。因此我們可以定義一個這樣的Response
類
public class Response<T> {
private String resultcode;
private String reason;
private T result;
}
複製程式碼
使用泛型來決定result最終向裡面填充什麼東西。當然,這裡所填充的是一個物件,姑且我們也可以將它定義為User
類
public class User {
private String area;
private String sex;
private String birthday;
}
複製程式碼
這樣,我們的資料就可以按照示例json的約定規則,返回給呼叫者了。
而作為呼叫方,也就是接收這條資料的機器,我們則需要定義如下的類ResopnseUser
public class ResopnseUser {
private String resultcode;
private String reason;
private User result;
}
public class User {
private String area;
private String sex;
private String birthday;
}
複製程式碼
然後我們可以使用fastJson來進行愉快的解析了
String testJson = "{\"resultcode\":\"200\",\"reason\":\"成功的返回\",\"result\":{\"area\":\"浙江省溫州市平陽縣\",\"sex\":\"男\",\"birthday\":\"1989年03月08日\"}}";
ResponseUser response1 = JSONObject.parseObject(testJson,ResponseUser.class);
複製程式碼
可以看到,也確實得到了我們想要的結果
思考改進措施
但是,這有個不太嚴重的後果。我每跟提供者對接一個介面,就得生成一個與之對應的類。久而久之,這些專用的類會變得越來越多,巢狀也會越來越深。既然提供者可以抽象出一個Response
String testJson = "{\"resultcode\":\"200\",\"birthday\":\"1989年03月08日\"}}";
Response<User> response = JSONObject.parseObject(testJson,Response.class);
複製程式碼
很遺憾,我們得到的結果並非所期望的,只有最外層的資料解析成功了,內層的result
仍是JSONObject
.
求助於萬能的論壇
幸運的事,在這萬千工程師中並非只有想到了這種處理辦法,並且已經有人解決這類問題了!
通過各種搜尋下來,我們發現了fastJson提供了TypeReference
這個類,可以解決我們的問題。
String testJson = "{\"resultcode\":\"200\",new TypeReference<Response<User>>(){});
複製程式碼
很好,我們已經得到了想要的結果,終於可以統一所有的介面最外層返回值的判斷了。
絕不止步於此
等等,就沒有發現什麼問題嗎?泛型在編譯時會執行型別擦除啊!這也就是我們第一次直接使用Response<User> response = JSONObject.parseObject(testJson,Response.class);
時無效的原因。
那麼,即使我們這麼寫了new TypeReference<Response<User>>(){}
不也一樣會被擦除掉嗎?執行時它是如何獲取到我們所定義的實際型別的?
通過檢視TypeReference
的原始碼,幸運的是該原始碼十分的簡單而且程式碼量少。首先進入的就是它的建構函式protected TypeReference()
,通過debug我們發現,在程式碼執行到第二行時,就已經獲得了我們所寫的泛型.
Type superClass = getClass().getGenericSuperclass();
Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
複製程式碼
這段程式碼很簡單,獲取到它的父類getGenericSuperclass()
就獲取到了實際的型別.繼續跟程序式碼,我們可以發現它呼叫了一個native方法private native String getGenericSignature0();
獲取到了該類和父類的一些資訊ClassRepository
和Type
。
此時,回過頭來我們再看new TypeReference<Response<User>>(){}
其實是建立了一個TypeReference
的匿名內部類,通過該類的getGenericSuperclass()
,獲取到了實際的型別資訊。
繼續深挖
讓我們現在回顧一下我們的所得,注要有2點:
- 泛型在編譯時會執行型別擦除。
- 獲取到它的父類
getGenericSuperclass()
就獲取到了實際的型別。 等等,這兩點是互相矛盾的啊。1說沒有,2又說還能找到,到底是怎麼回事呢?通過檢視編譯後的位元組碼檔案,我們找到了答案。 這是包含程式碼Response<User> response = JSONObject.parseObject(testJson,new TypeReference<Response<User>>(){});
的方法的class檔案反編譯後的資訊。我們可以看到 它多了一個LocalvariableTypeTable
,裡面的Signature
正好儲存了實際的型別資訊。**也就是說型別並未完全擦除,我們依然可以通過反射的方式拿到引數的型別。**所謂的擦除,只是把方法code
屬性的位元組碼進行擦除。 另外,我們還看到了InnerClasses
列表中,有一個內部類,叫Main$1,也就是我們的這個new TypeReference<Response<User>>(){}
總結
- 可以通過
JSONObject.parseObject(testJson,new TypeReference<Response<User>>(){});
的方式,解決json中巢狀泛型的問題。方便、快捷、直觀 - 可以通過
getClass().getGenericSuperclass();
獲取到真實型別。驗證程式碼
// 這類建立了一個HashMap的匿名子類
HashMap<String,Integer> subIntMap = new HashMap<String,Integer>(){};
System.out.println(subIntMap.getClass().getSuperclass());
Type subClassType = subIntMap.getClass().getGenericSuperclass();
if(subClassType instanceof ParameterizedType){
ParameterizedType p = (ParameterizedType) subClassType;
for (Type t : p.getActualTypeArguments()){
System.out.println(t);
}
}
複製程式碼
輸出結果
class java.util.HashMap
class java.lang.String
class java.lang.Integer
複製程式碼
- 型別並未完全擦除,可以通過反射的方式拿到引數的型別。驗證程式碼
ResponseUser response = new ResponseUser();
Field field = response.getClass().getField("result");
System.out.println("result Type is "+ field.getType());
System.out.println("result GenericType is "+ field.getGenericType());
public class ResponseUser {
private String resultcode;
private String reason;
public List<User> result;
}
複製程式碼
輸出結果
result Type is interface java.util.List
result GenericType is java.util.List<com.jd.jr.alpha.cpa.fastJson.User>
複製程式碼