2013年6月3日

【Android】解析JSON格式文字進行資料讀取

哈囉~~~各位Android安卓開發者大家好呀!

今天小黑人要與大家分享的是繼上一篇文章(Server API網路串接傳輸HttpGet與HttpPost的運用)所說的JSON格式解析,例如我們與Server API串接溝通後Server會回傳一串文字,而這串文字通常都會定義為JSON格式,那JSON格式文字是什麼呢?要怎麼讀取回傳文字裡面的資訊呢?這個主題就是今天小黑人要與大家分享的內容,可能在大家耳裡常聽過JSON,但JSON其中又包含JSONObjectJSONArray,那這兩者又要怎麼解析運用的,就跟著小黑人讓我們繼續看下去吧。

首先,今天小黑人打算用解析JSON的方法來告訴大家JSON內部的真正結構,所以大家必須先搭配上一篇文章,取得JSON格式文字後再藉由此文章進行文字解析。

JSON是什麼?其實說簡單一點JSON只是一個通稱,它的組成是由JSONObject或JSONArray所組成的,而其中的JSONObject其實就是一個JSON格式的物件JSONArray就是JSONObject所組成的List列表,一個JSONObject都是包含一個欄位Key與內容(格式如- "欄位Key":"資訊內容"),JSONArray則是許多的"{ }"每個都是由JSONObject組成,每個{ }與{ }間用逗號區隔(格式如- {{"欄位Key":"資訊內容"},{"欄位Key":"資訊內容"},{"欄位Key":"資訊內容"}}),所以JSON就是以這樣的方式來組成,既然JSON可以靈活的組裝也當然可以擁有很多種變化,舉例來說,像JSONObject內包含JSONObject、JSONObject內包含JSONArray、JSONArray內包含JSONObject...等等各種類型一層一層的包覆,相對的要解析也是要從結構內一層一層解開讀取,接下來我們就藉由實例來向大家說明要如何解析讀取吧。

實作部分小黑人分成三種JSON格式文字範例來向大家說明該如何進行解析 :
1.第一種類型為JSONObject內又包覆一個JSONObject。
//例如Server API回傳的mJSONText文字為下列
{"id":"1","name":"小黑人","title":"解析JSON格式文字 by 小黑人的Android教室" ,"info":{"love":"android","color":"Black黑色","say":"歡迎大家來到小黑人的Android教室 ^^"}}
我們先依序取得"id"、"name"、"title"、"info",這裡比較特別的是"info",因為"info"裡面又包覆著一個JSONObject,所以在"info"裡必須再解析一次JSONObject。下列為我們依序取出欄位值的作法。
//建立一個JSONObject並帶入JSON格式文字,getInt取出"id"欄位的數值
int ID = new JSONObject(mJSONText).getInt("id");

//建立一個JSONObject並帶入JSON格式文字,getString取出"name"與"title"欄位的數值
String NAME = new JSONObject(mJSONText).getString("name");
String TITLE = new JSONObject(mJSONText).getString("title");

//相同的方法在info這裡讀取出的又是一串JSON格式文字,原因就是這裡還有一層JSONObject
String INFO = new JSONObject(mJSONText).getString("info");

//所以我們要讀取JSONObject裡的JSONObject資訊就必須解析兩層JSONObject
//首先先把外層的info解析出來再解析內層的"love"、"color"、"say"欄位
String LOVE = new JSONObject(new JSONObject(mJSONText).getString("info")).getString("love");
String COLOR = new JSONObject(new JSONObject(mJSONText).getString("info")).getString("color");
String SAY = new JSONObject(new JSONObject(mJSONText).getString("info")).getString("say");
這樣就可以把全部的欄位資訊都讀取出來囉
印出的結果是 :
ID = 1 ,
NAME = 小黑人 ,
TITLE = 解析JSON格式文字 by 小黑人的Android教室 ,
LOVE = android ,
COLOR = Black黑色 ,
SAY =歡迎大家來到小黑人的Android教室 ^^ 。
2.第二種類型為JSONObject內包覆一個JSONArray。
//例如Server API回傳的mJSONText文字為下列
{"id":"2","name":"小黑人","title":"解析JSON格式文字 by 小黑人的Android教室" ,"info":{{"lesson":"第一篇","data":"ImageView運用篇"},{"lesson":"第二篇","data":"TextView運用篇"},{"lesson":"第三篇","data":"Gallery運用篇"}}}
我們一樣依序取得"id"、"name"、"title"、"info",然後在"info"裡面包覆著一個JSONArray,所以在"info"裡必須解析JSONArray,讓JSONArray解析變成許多的JSONObject。下列為我們依序取出欄位值的作法。
//建立一個JSONObject並帶入JSON格式文字,getInt取出"id"欄位的數值
int ID = new JSONObject(mJSONText).getInt("id");

//建立一個JSONObject並帶入JSON格式文字,getString取出"name"與"title"欄位的數值
String NAME = new JSONObject(mJSONText).getString("name");
String TITLE = new JSONObject(mJSONText).getString("title");

//相同的方法在info這裡讀取出的是一串JSON格式文字的JSONArray
String INFO = new JSONObject(mJSONText).getString("info");

//要取出info裡的JSONArray欄位值必須先getJSONObject,依照JSONArray的size並帶入所想要取出資訊的JSONObject位置,且依序取出來後再進行讀取內部欄位資訊("lesson"與"data")
String LESSON1 = new JSONArray(new JSONObject(mJSONText).getString("info")).getJSONObject(0).getString("lesson");
String DATA1 = new JSONArray(new JSONObject(mJSONText).getString("info")).getJSONObject(0).getString("data");
                                       
String LESSON2 = new JSONArray(new JSONObject(mJSONText).getString("info")).getJSONObject(1).getString("lesson");
String DATA2 = new JSONArray(new JSONObject(mJSONText).getString("info")).getJSONObject(1).getString("data");

String LESSON3 = new JSONArray(new JSONObject(mJSONText).getString("info")).getJSONObject(2).getString("lesson");
String DATA3 = new JSONArray(new JSONObject(mJSONText).getString("info")).getJSONObject(2).getString("data");
這樣就可以把全部的欄位資訊都讀取出來囉
印出的結果是 :
ID = 2 ,
NAME = 小黑人 ,
TITLE = 解析JSON格式文字 by 小黑人的Android教室 ,
LESSON1 = 第一篇 ,
DATA1 = ImageView運用篇 ,
LESSON2 = 第二篇 ,
DATA2 = TextView運用篇 ,
LESSON3 = 第三篇 ,
DATA3 = Gallery運用篇 。
3.第三種類型為JSONArray內包覆一個JSONObject。
//例如Server API回傳的mJSONText文字為下列
{{"id":"1","lesson":"第一篇","data":"歡迎來到小黑人的Android教室-現在要上第一節課"},
{"id":"2","lesson":"第二篇","data":"歡迎來到小黑人的Android教室-現在要上第二節課"},
{"id":"3","lesson":"第三篇","data":"歡迎來到小黑人的Android教室-現在要上第三節課"}}
這段的解析方式就比較不一樣,我們必須先取出JSONArray中的JSONObject,而JSONObject會依照JSONArray的size長度依序讀取,JSONObject取出後再讀取欄位的資訊,下列為我們依序取出欄位值的作法。
//先取出JSONArray中的第幾個JSONObject,由getJSONObject帶入進行取出,再get欄位資訊取得資料
int ID1 = new JSONArray(mJSONText).getJSONObject(0).getInt("id");
String LESSON1 = new JSONArray(mJSONText).getJSONObject(0).getString("lesson");
String DATA1 = new JSONArray(mJSONText).getJSONObject(0).getString("data");
                                       
int ID2 = new JSONArray(mJSONText).getJSONObject(1).getInt("id");
String LESSON2 = new JSONArray(mJSONText).getJSONObject(1).getString("lesson");
String DATA2 = new JSONArray(mJSONText).getJSONObject(1).getString("data");
                                       
int ID3 = new JSONArray(mJSONText).getJSONObject(2).getInt("id");
String LESSON3 = new JSONArray(mJSONText).getJSONObject(2).getString("lesson");
String DATA3 = new JSONArray(mJSONText).getJSONObject(2).getString("data");
這樣就可以把全部的欄位資訊都讀取出來囉
印出的結果是 :
ID1 = 1 ,
LESSON1 = 第一篇 ,
DATA1 = 歡迎來到小黑人的Android教室-現在要上第一節課 ,
ID2 = 2 ,
LESSON2 = 第二篇 ,
DATA2 = 歡迎來到小黑人的Android教室-現在要上第二節課 ,
ID3 = 3 ,
LESSON3 = 第三篇 ,
DATA3 = 歡迎來到小黑人的Android教室-現在要上第三節課 。
以上就是解析JSON格式文字的各種讀取方式,主要還是要依照自己本身與Server API溝通時所訂下的規則而定,前面所說的三種類型範例是要讓大家了解JSON的架構和取出的方法,大家可以試試看唷^^
謝謝大家~如有任何問題都可以和小黑人一起交流討論喔~! XDD

28 則留言:

  1. 你好~
    想請問一下,如果我是想要將android上的字串利用json傳給php網頁要怎麼做呢?

    回覆刪除
    回覆
    1. 您好,很抱歉這麼久才回覆您!
      哈哈,小黑人這段時間忙著處理專題研究,真不好意思...
      根據您的提問小黑人向您解釋,
      與PHP後台串接其實有兩種方式可以進行,
      第一種是不需要傳資訊給後台的方式 - Get傳輸。
      第二種是需要帶參數給後台的方式 - Post傳輸。
      詳細傳輸串接方法,小黑人這幾天會上傳新文章說明,
      再麻煩您檢視。

      感謝您的提問!

      刪除
  2. 您好,很抱歉這麼久才回覆您!
    哈哈,小黑人這段時間忙著處理專題研究,真不好意思...
    根據您的提問小黑人向您解釋,
    與PHP後台串接其實有兩種方式可以進行,
    第一種是不需要傳資訊給後台的方式 - Get傳輸。
    第二種是需要帶參數給後台的方式 - Post傳輸。
    詳細傳輸串接方法,小黑人這幾天會上傳新文章說明,
    再麻煩您檢視。

    感謝您的提問!

    回覆刪除
  3. 請問大大
    如果要取得全部的JSON的KEY 有方法嗎?

    回覆刪除
    回覆
    1. 您好,根據您的問題小黑人向您解釋,
      取得Json的全部文字很容易,其中也包含Key與內容,
      但關鍵是在解析的部分,依照Key欄位的不同取出不同資料,
      而這個Key就是前端開發人員與後端開發人員要協調制定的,
      所以通常針對的是內容而不會是Key,但如果要還是要取出所有的Key值的話,
      可能要以Json的全部字串來進行拆解,再用Json固定的規則把Key與內容分開取出。

      感謝您的提問!

      刪除
    2. 謝謝大大的回復!!

      刪除
  4. @@ 恩...
    互相學習一下,我怎麼查到的是JSONArray應該用中括號[ ]包起來,用逗號,區隔每個內容物,而且應該是沒有索引的?
    JSONObject是大括號{ }括起來,用逗號,區分元素,每個元素格式NAME : VALUE。
    參考來源http://www.ietf.org/rfc/rfc4627.txt

    回覆刪除
    回覆
    1. 您好,很抱歉這麼久才回覆您,
      根據您的疑問小黑人是覺得應該是語言與符號的差異,
      只要能正確讀取資料且完整就行囉,
      當然json的欄位是沒有索引的,而形態也是同您說的NAME:VALUE,
      感謝您的留言!

      刪除
  5. JSONArray json = new JSONArray(dataJson);
    dataJson大概有1萬兩千筆資料,我發現解析的速度似乎沒有很快,
    不知道有沒有更快的方法?

    回覆刪除
    回覆
    1. 您好,根據您的問題小黑人與您解釋,
      其實如果資料量很龐大的話建議您不要一次把資料全部抓下來,
      全抓下來一方面會耗時間一方面會佔據太多記憶體資源,
      建議您分段方式進行下載,一次抓個50筆或多少筆之類,
      分開下載速度就會快了.

      感謝您的提問!

      刪除
    2. 如何只抓部分json 可以限制網頁只給多少嗎?

      刪除
    3. 如何只抓部分json 可以限制網頁只給多少嗎?

      刪除
  6. 你用得太多new 了

    回覆刪除
    回覆
    1. 您好,非常感謝您的建議,
      其實可以只用new一個String出來,其他再藉由這個String來取得資料,
      或者是運用Gson工具來解析JSON格式文字。

      感謝您的留言!

      刪除
  7. 你好 我想請問關於httpget ,由於我在網路上參考的範例使用Anytask (將httpget google map api)算距離,可是我想要將資料庫中執行的語法 逐一算出與現在位置的距離差 ,有沒有比較好的辦法呢 ... 已經是想破頭了,所以才求助於您。

    回覆刪除
    回覆
    1. 您好,很抱歉這麼久才回覆您,
      根據您的提問小黑人與您解釋,
      其實您可以分成兩個部份來實作,
      第一是藉由HttpGet取得目標資訊(如經緯度位置),
      第二是資料取得完畢後再進行距離判斷,
      如果最後要將距離放入資料庫的話可判斷完距離後再存入資料庫。

      距離判斷與資料庫運用小黑人文章都有教學,您可以看考看看~

      感謝您的提問!

      刪除
  8. 你真的寫錯了, JSONArray是用[]包起來的

    回覆刪除
    回覆
    1. 您好,非常感謝您的提醒,
      沒錯,Array的外層是用 [ ] 來包裝的,
      哈,小黑人竟然沒有注意到,謝謝。

      感謝您的留言!

      刪除
  9. 想請問小黑人老師 如果我從網路上抓取JSON 但是 此JSON的資料量很大 造成無法一次抓完全部 那該如何處理呢 (目前 抓資料量不大的JSON都沒問題)

    回覆刪除
  10. 你好,請問~
    在Android中分段抓取json資料該如何處理?
    只需修改PHP還是android的程式碼也需要改呢?
    又需要改什麼?

    回覆刪除
  11. 很不錯的教學!讓我的問題迎刃而解

    回覆刪除
  12. 大大 首先感謝您的教學 請問有辦法讓json UTF8轉成中文顯示嗎

    -------------------------(以下程式碼)-------------------------



    -------------------(以下chore開啟url結果)------------------
    [{"id":"1","title":"myhome","coords":"23.702757, 120.535848","pic":"https:\/\/lh3.ggpht.com\/A0x3jzuH1qRkE10HcTiT4qQr_6iAqVg-CTsoIqxnoIFyv92V91WI3KqiVlOvLtfoMRg=w300","type":"landscape","addr":"Yunlin Conty"}]
    [{"id":"2","title":"myschool","coords":"23.695493, 120.536780","pic":"https:\/\/encrypted-tbn3.gstatic.com\/images?q=tbn:ANd9GcRwN-XOVewGNUj23oHBqT8TELi2qx30LynjtnIUWXUlbQN2Aewr","type":"store","addr":"\u96f2\u6797\u7e23\u6597\u516d\u5e02\u5927\u5b78\u8def\u4e09\u6bb5123\u865f"}]

    ------------------------------------------------------------------------------------------

    回覆刪除
  13. 程式碼補上:

    header('Content-Type: application/json; charset=utf-8');
    if(isset($_REQUEST['SpotID']))
    {
    $con = mysql_connect('localhost','root','');
    if (!$con)
    {
    die('Could not connect: ' . mysql_error());
    }


    mysql_select_db("myspots_test", $con);
    mysql_query("SET NAMES 'utf8'");

    $spotID = $_REQUEST['SpotID'];
    $result = mysql_query("SELECT * FROM `myspots` WHERE id = '$spotID' ") or die ('Errant query:');

    while($row = mysql_fetch_assoc($result))
    {
    $output[]=$row;
    }

    print(json_encode($output));

    mysql_close($con);
    }
    else
    {
    $output = "not found";
    print(json_encode($output));

    }

    回覆刪除
    回覆
    1. 問題解決了 給有出現類似問題的參考:
      header('Content-Type: application/json; charset=utf-8');
      if(isset($_REQUEST['SpotID']))
      {
      $con = mysql_connect('localhost','root','');
      if (!$con)
      {
      die('Could not connect: ' . mysql_error());
      }


      mysql_select_db("myspots_test", $con);
      mysql_query("SET NAMES 'utf8'");

      $spotID = $_REQUEST['SpotID'];
      $result = mysql_query("SELECT * FROM `myspots` WHERE id = '$spotID' ") or die ('Errant query:');

      while($row = mysql_fetch_assoc($result))
      {
      foreach ($row as $key => $value) {
      $row[$key] = urlencode($value);
      }
      $output[]=$row;
      }

      echo urldecode(json_encode($output));

      mysql_close($con);
      }
      else
      {
      $output = "not found";
      print(json_encode($output));

      }

      刪除
  14. 小黑人大大您好
    感謝您提供的資料
    這邊有些建議想提供給您
    我使用第一種類型JSONObject
    出現了JSONException
    提醒您要加上 try catch
    這邊也提供給其他人參考 可以省下許多debug時間哦

    回覆刪除
  15. 小黑人大大您好
    我目前遇到一個情況想請教您
    有個json檔如下:
    var JsonString = [{
    "id": 1,
    "AvgPerf": 788.45,
    "MaxPerf": 0.0,
    "MinPerf": 0.0,
    "AvgPerf_2": 6535.77,
    },
    {
    "id": 2,
    "AvgPerf": 349.45,
    "MaxPerf": 0.0,
    "MinPerf": 0.0,
    "AvgPerf_2": 3135.77,
    }
    ];
    我只想抓出所有"id"和"AvgPerf"的數值分別匯入x,y座標點的形式來繪圖
    以下是許多現有繪圖功能的部分程式碼
    dataPoints: [
    { x: 1, y: 21 },
    { x: 2, y: 44 },
    { x: 3, y: 35 },
    { x: 4, y: 45 },
    { x: 5, y: 75 },
    { x: 6, y: 58 },
    { x: 7, y: 18 },
    { x: 8, y: 30 },
    { x: 9, y: 11}
    ]
    如上都是給訂好數值寫死的
    請問厲害的您知道如何將json內的資料傳送至以上數字格式來畫圖嗎?
    若您能指點 感激不盡!!!

    回覆刪除
  16. 請問看到這樣的錯誤訊息該從哪裡開始除錯?
    <br of type java.lang.String cannot be converted to JSONObject
    應該是php回傳android出錯可是著不到錯誤...

    回覆刪除

謝謝大家支持,有任何問題都可以和小黑人一起討論!