代碼在最後java
我我的是不太喜歡http和json,多是遊戲作的多了的緣由的,對通訊協議和通訊方式特敏感,所以即便是作應用我也會選擇rpc而非http,可是有時候由於各類緣由,仍是不的不處理標準的http+json的東西。git
這一次也確實須要處理一大串json,就是將一大堆的json轉換成標準的java pojo。也許小json串咱們能夠直接用JSONObject去提值就好了,可是若是json是這樣:github
一個擁有近百個不同的字段的pojo,若是咱們須要單獨的去取值估計會瘋掉,這還不是主要的,更蛋疼的是pojo自己的屬性又是array或者其餘pojo,這樣依次嵌套,估計我已經瘋了。另外還有一個問題就是若是字段名稱一修改,就的手動去修改get的那個名稱,徹底是苦力活。json
(ps:上面那個圖,是nutch+es返回的值,我本身弄了一個搜索引擎玩,全部返回有大坨的數據,可是我實際處理的不是這個數據,這裏只是用它舉例,本身搞的搜索引擎在:服務器
http://search.bucry.com/ 純粹是爲了好玩而已)搜索引擎
可是我又不得不面對這個問題,就是把這一大串json弄成pojo,因而我天然想到偷懶,想用一個東西自動的將它封裝成pojo,自動識別pojo的字段,自動從json中去取,而且自動調用set賦值,那麼即便後面修改了字段名稱,又怎樣?無所謂,它自己就是反射,因而開始動手作,我須要解決的問題以下:spa
1.遍歷pojo的屬性,拿到它的屬性的這個變量的名稱code
2.根據屬性的名稱,從JSONObject裏面去get值blog
3.在JSONObject裏面get值的時候是須要知道變量的類型的,若是它是一個pojo,那麼繼續遞歸調用走 1遞歸
4.在JSONObject裏面get值的時候若是是一個List活着Array那麼使用JSONArray,而後經過String取出值,再判斷String,遞歸走2
5.反射調用set方法賦值
6.包裝成功
要解決上面的問題,首先我想到的是反射,可是反射在將其反射的時候必須知道類全路徑,因而我這個東西有其侷限性:
pojo類必須有這個字段:
private String className = RowResponse.class.getName();
也就是服務器在tojson的時候把這個字段傳給客戶端,客戶端在原封不動的傳送給服務器,那麼就可以成功的經過遞歸自動封裝全部的pojo,有人會說這樣多一個字段數據量會增大,會使通訊變慢的,這裏我想說的是,json已經大到我須要這樣去處理pojo的程度了,還管個卵的速度,這一大坨的東西註定它快不了。
首先咱們必須有兩個方法,一個是處理JSONObject,另外一個是處理JSONArray 的,而後它們之間會相互交叉調用,它們自己會相互遞歸調用:
public Object translateFromJson(JSONObject jsonObject) throws Exception { JSONType jsonType = JSONType.JSONOBJECT; Class<?> baseClass = Class.forName(jsonObject.getString("className")); Object object = baseClass.newInstance(); Field[] fields = baseClass.getDeclaredFields(); for (Field filed : fields) { Class<?> filedType = filed.getType(); Object filedValue = null; if ("serialVersionUID".equals(filed.getName())) { continue; } if (filedType.getCanonicalName().contains("int") || filedType.getCanonicalName().contains("Integer")) { filedValue = jsonObject.getInt(filed.getName()); } else if (filedType.getCanonicalName().contains("String")) { filedValue = jsonObject.getString(filed.getName()); } else if (filedType.getCanonicalName().contains("List")) { jsonType = JSONType.JSONARRAY; filedValue = jsonObject.getJSONArray(filed.getName()); } else if (filedType.getCanonicalName().contains("Long") || filedType.getCanonicalName().contains("long")) { filedValue = jsonObject.getLong(filed.getName()); } else if (filedType.getCanonicalName().contains("Double") || filedType.getCanonicalName().contains("double")) { filedValue = jsonObject.getDouble(filed.getName()); } else if (filedType.getCanonicalName().contains("Boolean") || filedType.getCanonicalName().contains("boolean")) { filedValue = jsonObject.getBoolean(filed.getName()); } else { jsonType = JSONType.JSONOBJECT; filedValue = jsonObject.getJSONObject(filed.getName()); } if (filedValue == null || filedValue.toString().length() == 0) { continue; } if (!filedValue.toString().contains("[{") && !filedValue.toString().contains("]}") && !filedValue.toString().contains("className")) { String firstMethodNameChar = filed.getName().substring(0, 1); String methodName = "set" + firstMethodNameChar.toUpperCase() + filed.getName().substring(1, filed.getName().length()); Method method = baseClass.getMethod(methodName, filed.getType()); method.invoke(object, filedValue); } else if (filedValue.toString().contains("className")) { Object subClassObject = null; switch (jsonType) { case JSONARRAY: subClassObject = translateFromJson((JSONArray)filedValue); break; case JSONOBJECT: subClassObject = translateFromJson((JSONObject)filedValue); break; } String firstMethodNameChar = filed.getName().substring(0, 1); String methodName = "set" + firstMethodNameChar.toUpperCase() + filed.getName().substring(1, filed.getName().length()); Method method = baseClass.getMethod(methodName, filed.getType()); method.invoke(object, subClassObject); } else { Object subClassObject = null; switch (jsonType) { case JSONARRAY: subClassObject = translateFromJson((JSONArray)filedValue); break; case JSONOBJECT: subClassObject = translateFromJson((JSONObject)filedValue); break; } String firstMethodNameChar = filed.getName().substring(0, 1); String methodName = "set" + firstMethodNameChar.toUpperCase() + filed.getName().substring(1, filed.getName().length()); Method method = baseClass.getMethod(methodName, filed.getType()); method.invoke(object, subClassObject); } } return object; }
public Object translateFromJson(JSONArray jsonObject) throws Exception { List<Object> outputStringList = new LinkedList<Object>(); for(int i=0; i<jsonObject.length(); i++){ String filedValue = jsonObject.get(i).toString(); if (filedValue.contains("className")) { JSONObject jsonObject1 = new JSONObject(filedValue); outputStringList.add(translateFromJson(jsonObject1)); } else { outputStringList.add(filedValue); } } return outputStringList; }
處理過程以下:
1.根據className反射出了這個類的一個實例,因爲是進入JSONObjct那麼它必定是pojo,不然它就是基本數據類型,是不可能進入該方法的
2.遍歷實例的全部屬性而且從JSONObject去取值
3.經過反射的getType方法得到對應的類,這裏須要區分基本類型與包裝類型
4.若是是List那麼就走array的方法,若是是JSONObject那麼繼續遞歸本身
5.JSONArray直接解析,若是拿出的 String包含className,那麼它是pojo繼續遞歸JSONObject,不然結束,直接add成ArrayList<Object>
6.若是是基本數據類型,那麼直接經過反射調用set 賦值
7.若是是List,那麼在遞歸後JSONArray會返回一個List<Object> 直接set
到這裏,基本搞定了,而後近百號字段也可以自動封裝了,反正省去了我一大把的去get值的時間。