關於GSON的入門級使用,這裡就不提了,如有需要可以看這篇博文 《Google Gson的使用方法,實現Json結構的相互轉換》 ,寫的很好,通俗易懂。
我為什麼寫這篇文章呢?因為前幾晚跟好友 xiasuhuei321 探討了一下GSON解析複雜的JSON的時候,能不能只解析源數據中的數組,甚至只解析數組的某一部分。探討了二十分鐘,得出結論:沒用過,不知道。
所以今天特地研究了一下,發現真的So Easy!之前想複雜了,學習的過程中,發現有五種方式分別搞定不同情況的JSON數組,也就是今天說的五大招!
在介紹之前先來個約定,比如下面的這個JSON:
"muser": [ { "name": "zhangsan", "age": "10", "phone": "11111", "email": "11111@11.com" }, ...]
開始過招吧!
第一招 A沒有數據頭的純數組JSON如何解析?根據約定,也就是這個 JSON 裡面只有一個數組(JsonArray),而且這個數組沒有名字,比如像下面這樣的:
[ { "name": "zhangsan", "age": "10", "phone": "11111", "email": "11111@11.com" }, { "name": "lisi", "age": "20", "phone": "22222", "email": "22222@22.com" }, ...]
這裡其實是最簡單的一種 JSON 數組格式,強大的 GSON 可以直接解析成一個 List 。但在這裡我先不直接解析,就用比較老實的方法去解析,因為需要引出兩個東西。
首先我們需要建立一個Bean對象,注意變量名要跟欄位名稱一致,沒什麼好說的:
public class UserBean { private String name ; private String age; private String phone; private String email; ...}
下面這是解析過程,先看代碼:
private void parseNoHeaderJArray() { String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_1); JsonParser parser = new JsonParser(); JsonArray jsonArray = parser.parse(strByJson).getAsJsonArray(); Gson gson = new Gson(); ArrayList<UserBean> userBeanList = new ArrayList<>(); for (JsonElement user : jsonArray) { UserBean userBean = gson.fromJson(user, UserBean.class); userBeanList.add(userBean); } mainLView.setAdapter(new UserAdapter(this, userBeanList));}
從代碼中可以看出解析的步驟如下:
無論 JSON 來自本地還是網絡獲取,都要先將 JSON 轉成 String ;
需要一個 JSON 解析類對象將JSON的字符串轉成 JsonArray ,前提是我們知道 JSON 中只有純數組;
循環遍歷 JsonArray ,並用 GSON 解析成相應的對象。
代碼本身不難,容易看懂,但前面說到,這裡我故意這樣寫,因為需要說兩個東西:
1、JsonParse
從名稱我們就可以看出,這是一個解析類。沒錯,它可以把 JSON 數據分別通過getAsJsonObject 和 getAsJsonArray 解析成 JsonObject 和 JsonArray 。這跟普通的解析 JSON 差不多,不展開說。
2、JsonElement
這個類我是第一次見,它是一個抽象類,代表 JSON 串中的某一個元素,可以是JsonObject/JsonArray/JsonPrimitive/… 中的任何一種元素。
所以在上面的代碼中,我們可以看到它能把 JsonArray 中的每一個元素轉成 JsonObject ,甚至說它本身就是 JsonObject 。
好了,就為了說這兩個東西。記住,後面將會用到。
來看一下運行的圖吧,很簡單的東西,後面的二三都是這樣的效果,就不重複貼圖了:
第二招 Q有數據頭的純數組數據該怎麼解析?內容跟上面的 JSON 一模一樣,只不過加了一個名稱 「muser」 ,也就是約定好的 數據頭 :
{ "muser": [ { "name": "zhangsan", "age": "10", "phone": "11111", "email": "11111@11.com" }, { "name": "lisi", "age": "20", "phone": "22222", "email": "22222@22.com" }, ... ]}
有人說,這還不簡單,在第一招中的 getAsJsonArray 加一個字符串就是咯,就像這樣:
JsonArray jsonArray = parser.parse(strByJson).getAsJsonArray("muser");
思路是對的,但是不要忘了,數組裝在一個 { } 括起來的 JsonObject 裡。還記得上面的JsonParse 麼,它的 getAsJsonObject 可以做到這點,所以代碼就是這樣啦,很簡單就不再解釋了:
private void parseHaveHeaderJArray() { String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_2); JsonObject jsonObject = new JsonParser().parse(strByJson).getAsJsonObject(); JsonArray jsonArray = jsonObject.getAsJsonArray("muser"); Gson gson = new Gson(); ArrayList<UserBean> userBeanList = new ArrayList<>(); for (JsonElement user : jsonArray) { UserBean userBean = gson.fromJson(user, new TypeToken<UserBean>() {}.getType()); userBeanList.add(userBean); } mainLView.setAdapter(new UserAdapter(this, userBeanList));}
注意,這裡又引出了一個東西: TypeToken ,它是什麼呢?
3、TypeToken
這個東西很有意思,本來我不知道到是幹嘛的,看了看源碼,看不懂。後來無意發現它所在的包:
import com.google.gson.reflect.TypeToken;
哎喲我去, reflect 這不是反射麼,一下子就明白了。沒錯,它其實是一個匿名內部類,看一下官方解釋:
GSON 提供了 TypeToken 這個類來幫助我們捕獲(capture)像 List 這樣的泛型信息。Java編譯器會把捕獲到的泛型信息編譯到這個匿名內部類裡,然後在運行時就可以被 getType() 方法用反射的 API 提取到。
解釋的很官方,實際上就是一句 通俗但不嚴謹 的話,它將泛型 T 轉成 .class 。比如上面的TypeToken 經過 getType() 後就是 UserBean.class 。
好了,說到這裡基本鋪墊就完成了,再次強調一下:
對於上面的 JSON 完全可以直接通過 GSON 轉成 List ,不用這麼麻煩,我只是為了引出3個小知識。
第三招 W有數據頭的複雜數據該如何解析呢?簡單的說完了,鋪墊也鋪完了,來看一看複雜的吧:
{ "code": 200, "msg": "OK", "muser": [ { "name": "zhangsan", "age": "10", "phone": "11111", "email": "11111@11.com" }, { "name": "lisi", "age": "20", "phone": "22222", "email": "22222@22.com" }, ... ]}
這裡就不再是純數組數據了,還有兩個湊數的不知道幹嘛用的欄位,這裡也有數據頭,之前用的是笨方法,現在來真正見識一下GSON的威力吧。
第一步根據 JSON 建立 Bean ,注意這裡的 Bean 是返回所有欄位,因為 GSON 能直接解析成 List ,所以 Bean 是下面這樣的,同樣把佔地方的 get/set 省略:
public class ResultBean { private int code; private String msg; private List<UserBean> muser; public class UserBean{ private String name ; private String age; private String phone; private String email; ... } ...}
注意,這個 ResultBean 裡面有一個 UserBean 。 它雖然跟上面第一第二招雖然內容一樣,但是作用不一樣,這是作為 JsonArray 解析後存入 List 中的對象。
算了,有點拗口,直接上代碼吧:
private void parseComplexJArrayByCommon() { String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_3); ResultBean resultBean = new Gson().fromJson(strByJson,ResultBean.class); List<ResultBean.UserBean> userBeanList = resultBean.getMuser(); mainLView.setAdapter(new ResultAdapter(this, userBeanList));}
沒錯,就是這麼四句話搞定第一二招的內容。看出GSON的強大了吧,當然如果有人想不開只寫一句話的話:
mainLView.setAdapter(new ResultAdapter(this,new Gson().fromJson(JsonToStringUtil.getStringByJson(this,R.raw.juser_3),ResultBean.class).getMuser()));
我也是沒意見的,不過請對自己好一點,謝謝。
第四招 E只想解析複雜JSON中的數組或數組中的某部分內容怎麼辦?好了,來到重點了,這也是跟好友 xiasuhuei321 沒有討論出來的情況。
還是上面的JSON數據,這裡為了篇幅就不貼重複代碼了,假如我只想取 「muser」 這個數組中的年齡(age)大於30歲的怎麼辦?
OK,當然可以先全部解析,再從 List 中取。那假如我有一萬條數據呢?全部解析不是很麻煩呢?
所以一個思路就是第一二招中說的: 遍歷!
OK,你會問先遍歷還不是要讀一萬條,是的,還是要讀一萬條,但是假如我要把這些存入資料庫呢?假如一萬條數據中只有一條符合條件,難道我先存一萬條,再從資料庫中查詢麼?
當然這種情況是極端情況,但也說明了一個問題,不能所有情況下都先全部解析,假如有一萬個欄位,Bean還得寫多長…可怕。
現在來說一下完整的思路,也是我學習中思考的過程:
第一點肯定就是剛才提到的遍歷,這個很好理解,所以我們先要取這一個數組(JsonArray),那麼如何取呢?還記得之前提到的 JsonParse 麼,它的 getAsJsonArray() 可以傳入 數據頭 拿到數組,當然不要忘了最外面一層是個 JsonObject 。
JsonObject jsonObject = new JsonParser().parse(strByJson).getAsJsonObject();JsonArray jsonArray = jsonObject.getAsJsonArray("muser");
拿到數組以後,我們就可以遍歷了,經過第一二招的洗禮,相信在遍歷上,應該沒什麼問題了,使用的還是之前提到的 JsonElement 。
for (JsonElement user : jsonArray) { UserBean userBean = new Gson().fromJson(user, new TypeToken<UserBean>() {}.getType()); if (Integer.parseInt(userBean.getAge()) > 30) { userBeanList.add(userBean); }}
上面的代碼很簡單,也用到了之前提到的 TypeToken ,什麼意思就不用解釋了吧。
好了,完整的代碼如下:
private void parseComplexJArrayByDirect() { String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_3); List<UserBean> userBeanList = new ArrayList<>(); JsonObject jsonObject = new JsonParser().parse(strByJson).getAsJsonObject(); JsonArray jsonArray = jsonObject.getAsJsonArray("muser"); for (JsonElement user : jsonArray) { UserBean userBean = new Gson().fromJson(user, new TypeToken<UserBean>() { }.getType()); if (Integer.parseInt(userBean.getAge()) > 30) { userBeanList.add(userBean); } } mainLView.setAdapter(new UserAdapter(this, userBeanList));}
運行的結果圖如下:
可以看到,現在我們做到了只取 JSON 數據中數組中某一部分了。那麼擴展一下,只取 JSON 數據中的某一個數組中的某一個欄位呢?當然可以實現,不過還是留給大家自己思考吧,當然下面反人類的第五招也是可以解決這個問題的。
第五招 R如果一個 JSON 數據很很很複雜怎麼解析?什麼叫做複雜,這裡我簡單寫了個比較複雜的,有數據頭,一層嵌套一層,我還沒有寫數組呢:
{ "group": { "user": { "name": "張三", "age": "10", "phone": "11111", "email": "11111@11.com" }, "info": { "address": "北京", "work": "Android Dev", "pay": "10K", "motto": "先定一個小目標,比如我先賺一個億" } }}
三種方式解析:
第三招,全部解析出來;
第四招,要什麼解析什麼;
第五招,反人類的 JsonReader 。
至於為什麼反人類,不好說。大家看代碼就知道了,代碼很簡單,跟 XML 的解析差不多,是根據節點來的,至於怎麼用,還是那句話直接看代碼吧,確實處理起來邏輯清晰,但是代碼量上,真的不敢恭維。
只貼代碼不作解釋,如想詳細了解,看文末連結。
private void parseComplexJArrayByReader() throws IOException { String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_4); JsonReader reader = new JsonReader(new StringReader(strByJson)); try { reader.beginObject(); String tagName = reader.nextName(); if (tagName.equals("group")) { readGroup(reader); } reader.endObject(); } finally { reader.close(); }}private void readGroup(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { String tagName = reader.nextName(); if (tagName.equals("user")) { readUser(reader); } else if (tagName.equals("info")) { readInfo(reader); } } reader.endObject();}private void readUser(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { String tag = reader.nextName(); if (tag.equals("name")) { String name = reader.nextString(); nameText.setText(name); } else if (tag.equals("age")) { String age = reader.nextString(); ageText.setText(age); } ... else { reader.skipValue(); } } reader.endObject();}private void readInfo(JsonReader reader) throws IOException { reader.beginObject(); while (reader.hasNext()) { String tag = reader.nextName(); if (tag.equals("address")) { String address = reader.nextString(); addressText.setText(address); } else if (tag.equals("work")) { String work = reader.nextString(); workText.setText(work); } ... else { reader.skipValue(); } } reader.endObject();}
上面代碼有省略,因為好長…運行圖如下:
五招過完,多謝指教!
總結以上幾乎就是 JSO N數組的所有情況了,這五招也幾乎能全部搞定!不得不說,GSON 確實比較強大,強大在於可以將 JSON 直接解析成對象,比以前的手動去解析方便太多,當然 fastJson 也能實現這點,但是這東西還是官方的用的順手。
在學習的過程中,也是一步一步來的,所以文章也是學習的過程,從簡單的例子學到關鍵內容,再解決複雜情況。由於文章寫得倉促,如有疑問或錯誤,歡迎交流與指正,謝謝!