fastJson反序列化處理泛型 我能從中學到什麼

都會的json解析

在咱們平常的編碼工做中,經常會制定或者遇到這樣的json結構java

{
  "resultcode": "200",
  "reason": "成功的返回",
  "result": {
    "area": "浙江省溫州市平陽縣",
    "sex": "男",
    "birthday": "1989年03月08日"
  }
}
複製代碼

對於該接口的提供者,最外層的resultcode reasonresult 這三個元素都是有的。所以咱們能夠定義一個這樣的Responsejson

public class Response<T> {
    private String resultcode;

    private String reason;

    private T result;

}
複製代碼

使用泛型來決定result最終向裏面填充什麼東西。固然,這裏所填充的是一個對象,姑且咱們也能夠將它定義爲Userbash

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來進行愉快的解析了ui

String  testJson = "{\"resultcode\":\"200\",\"reason\":\"成功的返回\",\"result\":{\"area\":\"浙江省溫州市平陽縣\",\"sex\":\"男\",\"birthday\":\"1989年03月08日\"}}";
        ResponseUser response1 =  JSONObject.parseObject(testJson,ResponseUser.class);

複製代碼

能夠看到,也確實獲得了咱們想要的結果 編碼

思考改進措施

可是,這有個不太嚴重的後果。我每跟提供者對接一個接口,就得生成一個與之對應的類。長此以往,這些專用的類會變得愈來愈多,嵌套也會愈來愈深。既然提供者能夠抽象出一個Response類,那麼我是否也能夠向提供者那樣處理?spa

String  testJson = "{\"resultcode\":\"200\",\"reason\":\"成功的返回\",\"result\":{\"area\":\"浙江省溫州市平陽縣\",\"sex\":\"男\",\"birthday\":\"1989年03月08日\"}}";
        Response<User> response =  JSONObject.parseObject(testJson,Response.class);
複製代碼

很遺憾,咱們獲得的結果並不是所指望的,只有最外層的數據解析成功了,內層的result還是JSONObject. debug

求助於萬能的論壇

幸運的事,在這萬千工程師中並不是只有想到了這種處理辦法,而且已經有人解決這類問題了! 經過各類搜索下來,咱們發現了fastJson提供了TypeReference這個類,能夠解決咱們的問題。code

String  testJson = "{\"resultcode\":\"200\",\"reason\":\"成功的返回\",\"result\":{\"area\":\"浙江省溫州市平陽縣\",\"sex\":\"男\",\"birthday\":\"1989年03月08日\"}}";
        Response<User> response =  JSONObject.parseObject(testJson,new TypeReference<Response<User>>(){});
複製代碼

很好,咱們已經獲得了想要的結果,終於能夠統一全部的接口最外層返回值的判斷了。

毫不止步於此

等等,就沒有發現什麼問題嗎?泛型在編譯時會執行類型擦除啊!這也就是咱們第一次直接使用Response<User> response = JSONObject.parseObject(testJson,Response.class);時無效的緣由。 那麼,即便咱們這麼寫了new TypeReference<Response<User>>(){}不也同樣會被擦除掉嗎?運行時它是如何獲取到咱們所定義的實際類型的? 經過查看TypeReference的源碼,幸運的是該源碼十分的簡單並且代碼量少。首先進入的就是它的構造函數protected TypeReference(),經過debug咱們發現,在代碼執行到第二行時,就已經得到了咱們所寫的泛型.cdn

Type superClass = getClass().getGenericSuperclass();

        Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
複製代碼

這段代碼很簡單,獲取到它的父類getGenericSuperclass()就獲取到了實際的類型.繼續跟進代碼,咱們能夠發現它調用了一個native方法private native String getGenericSignature0();獲取到了該類和父類的一些信息ClassRepositoryType。 此時,回過頭來咱們再看new TypeReference<Response<User>>(){}實際上是建立了一個TypeReference的匿名內部類,經過該類的getGenericSuperclass(),獲取到了實際的類型信息。

繼續深挖

讓咱們如今回顧一下咱們的所得,注要有2點:

  1. 泛型在編譯時會執行類型擦除
  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>>(){}

總結

  1. 能夠經過JSONObject.parseObject(testJson,new TypeReference<Response<User>>(){});的方式,解決json中嵌套泛型的問題。方便、快捷、直觀
  2. 能夠經過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
複製代碼
  1. 類型並未徹底擦除,能夠經過反射的方式拿到參數的類型。驗證代碼
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>
複製代碼
相關文章
相關標籤/搜索