1. 程式人生 > 程式設計 >fastJson反序列化處理泛型 我能從中學到什麼

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

都會的json解析

在我們日常的編碼工作中,常常會制定或者遇到這樣的json結構

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

對於該介面的提供者,最外層的resultcode reasonresult 這三個元素都是有的。因此我們可以定義一個這樣的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();獲取到了該類和父類的一些資訊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>
複製程式碼