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

2021-03-06 Android技術之家
寫在前面

關於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 也能實現這點,但是這東西還是官方的用的順手。

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


相關焦點

  • Gson轉換json數據為對象
    可以通過Gson使用兩種方法,將json字符串轉換為對象,以下面該段報文做測試{ "id
  • JSON從入門到精通
    key:string類型value:任何基本類型或數據結構(2)Array[value,value...]value:任何基本類型或數據結構。比如:{"name":"李廣", "values":[1,2,45,"你好"] }3、JSON數據示例二、JSON IN JAVA1、json使用2、使用Map構建json
  • JSONQuery v0.2.5,更簡單的使用 Gson 解析 JSON
    , "comment_info > [2]", Post.class);//將選擇結果反序列化為普通對象數組Post[] postArray = JSONQuery.select(json, "comment_info", Post[].class);//將選擇結果反序列化為泛型類型List<Post>Type type = new TypeToken<List<Post
  • 【踩坑】數組轉json後的結果是數組還是對象?
    ,因為最近接口數據寫的比較多,經常遇到這種問題,如果僅是 PHP 裡面看,則可以都是數組...問題:數組轉json,有時是對象,有時是數組?情況一:$arr = [1,2,3];echo json_encode($arr);     $arr = [1=>1,2=>2,3=>3];echo json_encode($arr);此時如果需要返回的是數組,可以這麼處理
  • (實用篇)PHP JSON數組與對象的理解
    因此,前後端相關人員先對PHP的json_encode函數原理有必要的了解是最重要的一個環節。PHP中的array是個萬能的數據結構,並不像其它語言根據需要的場景會定義很多約束性的數據類型來描述結構,所以PHP程式設計師是很難給客戶端人員講清楚返回的是什麼結構的數據的。也就變成明明是通過PHP的數組數據編碼成json的,但輸出值有時是數組、有時是對象的情況。
  • jq:命令行下解析JSON的神器
    各種語言都有大量的JSON處理庫,比如fastjson,Json-lib,jsoniter,jackson,gson等,我們可以很方便就可以寫一個腳本獲取接口的Json信息,並通過這些類庫進行處理。雖然如此,有些同學可能還是嫌寫腳本太麻煩,有沒有一種很簡單就能上手就用,用完就扔的JSON工具呢?答案是肯定的。
  • 5步搞定android混淆
    , java.lang.String);}-keepclassmembers class * extends android.webkit.webViewClient {    public void *(android.webkit.webView, jav.lang.String);}#-#----2        理解完戰略級思想後
  • 如何再Apache Hive中解析 Json數組
    如何在 Apache Hive 中解析 Json 數組Hive 內部提供了大量的內置函數用於處理各種類型的需求。從內置的 UDF 可以看到兩個用於解析 Json的函數:get_json_object 和 json_tuple。
  • 一個簡單的Ajax功能(用到Jquery與Json)
    不存在任何培訓機構招生信息功能介紹:就是頁面已加載完成就通過ajax異步訪問伺服器然後去局部刷新頁面(也就是給從伺服器獲取的值存在頁面的下拉框中顯示如下圖:)                                                     效果圖:
  • 使用JSONObject生成和解析json
    構建json在eclipse中使用JSONObject需要引用org.json包,推薦通過maven引用,如果不會使用maven,搭建maven項目可參考這篇文章《使用Eclipse構建Maven項目 (step-by-step)》,引用json則參考這篇文章《maven引入json各種版本》。
  • 在PHP中處理JSON數組以及對象
    數組和對象,以及如何在 PHP 中指定他們。我們需要告訴 PHP 空對象是一個顯示對象,而不是數組 。通過使用顯示的 stdClass 對象,我們可以強制使用 json_encode 解析器正確的輸出空對象,而不是空數組。 遺憾的是,這個冗長的解決方案,是在 PHP 中實現目標的唯一方法. 並沒有空對象的「簡短」版本。對象數組Elasticsearch DSL 中的另一種常見模式是對象數組。
  • json for modern c++的使用
    json for modern c++是一款非常好用的json庫,具有語法直觀和使用簡單的特點,並且是用C++11標準編寫的,此外還支持STL和json容器之間的轉換,可謂集方便又強大。本文推薦給廣大C++程式設計師,相信學習完本文之後,在處理json時一定會得心應手。
  • 用 Python 操作 JSON 類型數據詳解
    JSON值可以是數字(整數或浮點數),字符串(在雙引號中),邏輯值(True 或False),數組(在中括號中),對象(在大括號中)和null。例如{ "age": 21,"graduated ":true }。JSON值的基本格式如下圖所示。圖 JSON值的基本格式3、JSON對象。
  • Javaweb-案例練習-JSON對象
    其實我們可以把多個圖書名稱用數組格式傳給瀏覽器,前端JS代碼通過遍歷這個數組,也是可以拿到這些name數據。現在,我們一般不用數組,而是用JSON這格式。本篇就來看看JSON是如何解決上一篇字符串傳給瀏覽器的代碼,以及JSON對象中常見的操作。
  • PHP數組的處理方法
    () - 根據鍵,以降序對關聯數組進行排序  array_multisort() 函數對多個數組或多維數組進行排序。>  6、數組與json格式的轉換:  json_encode():將數組轉換為json對象格式,追加參數:JSON_UNESCAPED_UNICODE對中文進行編碼;  json_decode():將json對象轉換為php對象格式;  json_decode( $obj , true ):將json對象轉換為數組
  • JSON詳解
    其中關鍵字是字符串,而值可以是字符串,數值,true,false,null,對象或數組數組結構以」[」開始,」]」結束。中間由0或多個以」,」分隔的值列表組成,語法結構如代碼。        從表中我們可以看到一共有五條數據,現在我們要從資料庫中取出這些數據,然後利用JSON.NET的JsonConvert對象序列化它們為json字符串,並顯示在頁面上。
  • 解析JSON數據-JSONObject以及GSON
    使用JSONObject進行解析這種解析方法很常見了,我之前一直都是用這種和方法的,適用於只需要解析某幾個數據的情況,例如我們需要上面的" pm2p5 "數據而不需要其他的,這種情況用JSONObject就會簡單很多,我們來介紹一下這種方法:我們假定需要解析三個數據:1.now.pm2p52.station.name3.station.pm2p5
  • 全網最全的SQL解析Json總結
    2.1 get_json_object•語法: get_json_object(json, args)            •對於json中的key.直接用'$.key'標識            •對於json中的數組.用'$.key[0]'標識取key這個數組索引為0的值.如果本身就是一個json數組,那麼使用'$.[0]'標識取第一個元素.
  • json其實不難,只需了解一點,就能輕鬆玩轉它
    工作過程中,經常需要使用json這種輕量級的數據交換格式,例如,通過它可以組織數據保存到配置文件,客戶端和服務端通過json格式來進行通信等,但是,針對特定的需求場景,需要設計怎樣的json格式比較合適呢,json到底可以支持多少種格式呢,有沒有一種簡單的概括,就能讓使用者輕鬆使用它呢!
  • PHP中JSON的應用
    比如我們將一個數組序列化後存放,就可以很容易的反序列化後應用。JSON也是如此,只不過他搭建的是客戶端Javascript和服務端PHP的交互橋梁。我們用PHP生成JSON後的字符串,然後把這個字符串傳給前臺Javascript,Javascirpt就可以很容易的將其反JSON然後應用。說通俗點,它真的很像數組。言歸正傳,如何使用JSON。PHP5.2開始內置了JSON的支持。