Android:用GSON 五招之內搞定任何JSON數組

寫在前面

我為什么寫這篇文章呢?因為前幾晚跟好友 xiasuhuei321 探討了一下GSON解析復雜的JSON的時候,能不能只解析源數據中的數組,甚至只解析數組的某一部分。探討了二十分鐘,得出結論:沒用過,不知道。

所以今天特地研究了一下,發現真的So Easy!之前想復雜了,學習的過程中,發現有五種方式分別搞定不同情況的JSON數組,也就是今天說的五大招!

在介紹之前先來個約定,比如下面的這個JSON:

"muser": [
    {
      "name": "zhangsan",
      "age": "10",
      "phone": "11111",
      "email": "11111@11.com"
    },
    ...
]
  • 這里的 “muser” ,也就是數組的名稱,稱它為數據頭,防止跟里面的 字段 有歧義;
  • 如果沒有數據頭,那就叫它純數據,或者純數組數據;
  • 代碼中用到的 JsonArray/JsonObject 等熟悉的類全部來自 GSON 。

開始過招吧!

第一招 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 {
    //變量名跟JSON數據的字段名需要一致
    private String name ;
    private String age;
    private String phone;
    private String email;
    ...
}

下面這是解析過程,先看代碼:

/**

  • 解析沒有數據頭的純數組 */ private void parseNoHeaderJArray() { //拿到本地JSON 并轉成String String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_1);

    //Json的解析類對象 JsonParser parser = new JsonParser(); //將JSON的String 轉成一個JsonArray對象 JsonArray jsonArray = parser.parse(strByJson).getAsJsonArray();

    Gson gson = new Gson(); ArrayList<UserBean> userBeanList = new ArrayList<>();

    //加強for循環遍歷JsonArray for (JsonElement user : jsonArray) {

     //使用GSON,直接轉成Bean對象
     UserBean userBean = gson.fromJson(user, UserBean.class);
     userBeanList.add(userBean);
    

    } mainLView.setAdapter(new UserAdapter(this, userBeanList)); }</code></pre>

    從代碼中可以看出解析的步驟如下:

    • 無論 JSON 來自本地還是網絡獲取,都要先將 JSON 轉成 String ;
    • 需要一個 JSON 解析類對象將JSON的字符串轉成 JsonArray ,前提是我們知道 JSON 中只有純數組;
    • 循環遍歷 JsonArray ,并用 GSON 解析成相應的對象。

    代碼本身不難,容易看懂,但前面說到,這里我故意這樣寫,因為需要說兩個東西:

    1、JsonParse

    從名稱我們就可以看出,這是一個解析類。沒錯,它可以把 JSON 數據分別通過 getAsJsonObjectgetAsJsonArray 解析成 JsonObjectJsonArray 。這跟普通的解析 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() { //拿到本地JSON 并轉成String String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_2);

    //先轉JsonObject JsonObject jsonObject = new JsonParser().parse(strByJson).getAsJsonObject(); //再轉JsonArray 加上數據頭 JsonArray jsonArray = jsonObject.getAsJsonArray("muser");

    Gson gson = new Gson(); ArrayList<UserBean> userBeanList = new ArrayList<>();

    //循環遍歷 for (JsonElement user : jsonArray) {

     //通過反射 得到UserBean.class
     UserBean userBean = gson.fromJson(user, new TypeToken<UserBean>() {}.getType());
     userBeanList.add(userBean);
    

    } mainLView.setAdapter(new UserAdapter(this, userBeanList)); }</code></pre>

    注意,這里又引出了一個東西: 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 省略:

    /**

  • Created by xiarui on 2016/8/30.
  • 返回所有結果的Bean */ 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;
     ...
    

    } ... }</code></pre>

    注意,這個 ResultBean 里面有一個 UserBean 。 它雖然跟上面第一第二招雖然內容一樣,但是作用不一樣,這是作為 JsonArray 解析后存入 List 中的對象。

    算了,有點拗口,直接上代碼吧:

    /**

  • 有消息頭 復雜數據 常規方式 */ private void parseComplexJArrayByCommon() { //拿到Json字符串 String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_3); //GSON直接解析成對象 ResultBean resultBean = new Gson().fromJson(strByJson,ResultBean.class); //對象中拿到集合 List<ResultBean.UserBean> userBeanList = resultBean.getMuser(); //展示到UI中 mainLView.setAdapter(new ResultAdapter(this, userBeanList)); }</code></pre>

    沒錯,就是這么四句話搞定第一二招的內容。看出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);
      
      } }</code></pre> </li>
    • 上面的代碼很簡單,也用到了之前提到的 TypeToken ,什么意思就不用解釋了吧。
    • </ul>

      好了,完整的代碼如下:

      /**
    • 有數據頭 復雜數據 截取方式 */ private void parseComplexJArrayByDirect() { //拿到JSON字符串 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)); }</code></pre>

      運行的結果圖如下:

      可以看到,現在我們做到了只取 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 的解析差不多,是根據節點來的,至于怎么用,還是那句話直接看代碼吧,確實處理起來邏輯清晰,但是代碼量上,真的不敢恭維。

      只貼代碼不作解釋,如想詳細了解,看文末鏈接。

      /**

    • 通過JsonReader的方式去解析 */ 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")) {
           //讀group這個節點
           readGroup(reader);
       }
       reader.endObject();
      
      } finally {
       reader.close();
      
      } }

    /**

    • 讀group這個節點 *
    • @param reader JsonReader */ 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(); }

    /**

    • 讀用戶基本消息 user節點 *
    • @param reader JsonReader */ 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(); }

    /**

    • 讀用戶其他消息 info節點 *
    • @param reader JsonReader */ 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(); }</code></pre>

      上面代碼有省略,因為好長…運行圖如下:

      五招過完,多謝指教!

      總結

      以上幾乎就是 JSO N數組的所有情況了,這五招也幾乎能全部搞定!不得不說,GSON 確實比較強大,強大在于可以將 JSON 直接解析成對象,比以前的手動去解析方便太多,當然 fastJson 也能實現這點,但是這東西還是官方的用的順手。

      在學習的過程中,也是一步一步來的,所以文章也是學習的過程,從簡單的例子學到關鍵內容,再解決復雜情況。由于文章寫得倉促,如有疑問或錯誤,歡迎交流與指正,謝謝!

      參考資料

      靈活組裝Json的數據使用Gson的JsonParser和JsonReader解析Json詳解例子

      使用Gson解析復雜的json數據 – tkwxty

      JsonElement的簡單說明 – chunqiuwei

      Java進階(四)Java反射TypeToken解決泛型運行時類型擦除的有關問題解決

       

       

      來自:http://www.iamxiarui.com/2016/08/30/android:用gson-五招之內搞定任何json數組/

       

 本文由用戶 RoscoeHolco 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!