Android下的Json學習指南

Json基礎知識

  • Json是什么

    Json是JavaScript Object Notation(JavaScript對象表示法)的縮寫,是一種輕量級的文本數(shù)據(jù)交換格式,是用來存儲和交換文本信息的語法。

    它獨立于語言且具有自我描述性,更容易理解。雖然使用Javascript語法來描述數(shù)據(jù)對象,但很多編程語言都支持Json。

    Json的文件類型是".json",MIME類型是"application/json"。

  • Json的語法

    典型的Json實例:

{ "photos": { "page": 1, "pages": 10, "perpage": 100, "total": "1000", 
    "photo": [
      { "id": "31624984467"}, {"owner": "155140314@N03"}, {"secret": "efa8606dc9"}]}}

Json語法是JS語法的子集:

  1. 數(shù)據(jù)在名稱/值對{ key:value }中
  2. 數(shù)據(jù)由逗號分隔
  3. 大括號保存對象
  4. 中括號保存數(shù)組

Json的key為字符串,值可以是:

  1. 數(shù)字(整數(shù)或浮點數(shù))
  2. 字符串(在雙引號中)
  3. 邏輯值(true 或 false)
  4. 數(shù)組(在中括號中)
  5. 對象(在大括號中)
  6. null
  • Json對象

    JSON 對象在大括號({ })中書寫,對象可以包含多個名稱/值對。名稱/值對中使用冒號(:)分割,每個名稱/值對對使用逗號(,)分割。

    一個典型的例子:
    {"id": "31624984467"}

  • Json數(shù)組

    JSON 數(shù)組在中括號中書寫,數(shù)組可包含多個對象,也可只是合法的基本數(shù)據(jù)類型:

    [{ "id": "31624984467"},{ "owner": "155140314@N03"}]

    ["a","b","c"]

Android中的Json解析

利用Android自帶的Json解析類

  • 使用JSONObject,JSONArray來解析:

    JSONObject類:生成Json對象,可以完成Json字符串與Java對象的相互轉換。

    JSONArray類:生成Json數(shù)組,可以完成Json字符串與Java集合或對象的相互轉換。

    JSONStringer類:Json文本構建類,這個類可以幫助快速和便捷的創(chuàng)建JSON text, 每個JSONStringer實體只能對應創(chuàng)建一個JSON text。

    JSONException類:Json異常。

    使用步驟介紹:

    1. 獲取Json字符串。一般是從網(wǎng)絡獲取,為了方便這里直接從本地獲取。
    2. 使用JSONObject類,利用Json字符串生成JSONObject對象。
    3. 使用getJSONObject,getJSONArray等方法一個個的將Json字符串分解,再從中摳出所需要的屬性。

舉個栗子:

josn數(shù)據(jù)為:

{ "photos": { "page": 1, "pages": 10, "perpage": 100, "total": "1000",
    "photo": [
{ "id": "31624984467", "owner": "155140314@N03","title": "Happy New Year 2019 : Happy New Year Happy New Year 2019", "url_s": "https:\/\/farm5.staticflickr.com\/4852\/31624984467_efa8606dc9_m.jpg"},
{ "id": "31624992207", "owner": "163032290@N04","title": "","url_s": "https:\/\/farm5.staticflickr.com\/4844\/31624992207_a3196f29b6_m.jpg"},
{ "id": "31624994507", "owner": "146047562@N03","title": "swt33.c33.kr????????????:swt33??:swt33???????,????????,?????????,?????????,????????","url_s": "https:\/\/farm5.staticflickr.com\/4819\/31624994507_0a022a924c_m.jpg"}]}

從資源目錄中獲取json數(shù)據(jù)

private String jsonFromLocal() throws IOException {

    InputStream inStr = getResources().openRawResource(R.raw.jsontest);
    InputStreamReader inReader = new InputStreamReader(inStr);
    BufferedReader bufferedReader = new BufferedReader(inReader);
    StringBuilder builder = new StringBuilder(" ");
    String str = null;
    while ((str = bufferedReader.readLine()) != null) {
        builder.append(str);
        builder.append("\n");
    }
    inReader.close();
    bufferedReader.close();
    return builder.toString();
}

接著就開始扣需要的屬性了。

//生成JSONObject對象
String json = jsonFromLocal();
JSONObject jsonBody = new JSONObject(json);
parseItem(items, jsonBody);`

private void parseItem(List<Item> items, JSONObject jsonBody) throws JSONException {//先獲取最外層的對象photos
    JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
//再獲取photos內(nèi)的對象數(shù)組photo
    JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
    for (int i = 0; i < 3; i++) {
//再依據(jù)具體的key來獲取需要的屬性
        JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
        Item item = new Item();
        item.setId(photoJsonObject.getString("id"));
        item.setCaption(photoJsonObject.getString("title"));
        item.setUrl(photoJsonObject.getString("url_s"));
        item.setOwner(photoJsonObject.getString("owner"));
        items.add(item);
    }
}

item類:

public class Item {
private String id;
private String url;
private String owner;
private String caption;
......(get,set等方法)
  • 使用JsonReader來解析

    這種方式有點類似XML文檔的解析,使用深度優(yōu)先遍歷,尋找到需要的標記然后消耗掉它。對象的解析從beginObject()開始,到endObject()結束。數(shù)組的解析從beginArray()開始,到endArray()結束。

    下面我們來看一看吧,json數(shù)據(jù)依舊是之前的!

        JsonReader reader = new JsonReader(new StringReader(json));//獲取reader對象
        try {
            reader.beginObject();//從Json流中消耗一個標記,開始解析對象
            while (reader.hasNext()) {//循環(huán)判斷當前數(shù)組或者對象是否有下一個元素
                String propertyName = reader.nextName();//獲取key值
                if (propertyName.equals("photos")) {
                    readPhotos(reader, items);
                } else {
                    reader.skipValue();//跳過下一個不需要的元素
                }
            }
            reader.endObject();//結束對象解析
        } finally {
            reader.close();
        }
    }
    
    private void readPhotos(JsonReader reader, List<Item> items) throws IOException {
        reader.beginObject();
        while (reader.hasNext()) {
            String propertyName = reader.nextName();
            if (propertyName.equals("photo")) {
               readPhoto(reader, items);
            } else {
                reader.skipValue();
            }
        }
        reader.endObject();
    }
    
    private void readPhoto(JsonReader reader, List<Item> items)  throws IOException {
        reader.beginArray();//開始數(shù)組解析
        String id = null,url_s = null,owner = null,title = null;
        while (reader.hasNext()) {
            PhotoItem item = new PhotoItem();
            reader.beginObject();
            while (reader.hasNext()) {
                String propertyName = reader.nextName();
                switch (propertyName) {
                    case "id":
                        id = reader.nextString();//獲取"id"的值,返回String類型
                        break;
                    case "url_s":
                        url_s = reader.nextString();
                        break;
                    case "owner":
                        owner = reader.nextString();
                        break;
                    case "title":
                        title = reader.nextString();
                        break;
                    default:
                        reader.skipValue();
                        break;
                }
            }
                item.setId(id);
                item.setUrl(url_s);
                item.setOwner(owner);
                item.setCaption(title);
            items.add(item);
            reader.endObject();
        }
        reader.endArray();//結束數(shù)組解析
    }`
    
    

注意:
如果使用了nextName(),而沒有使用next **()( **可以是String,Int,Long等)方法或者skipValue(),這時候就會觸發(fā)異常,這一組方法需要配套使用。同理使用了beginObject()就要使用endObject()結尾,使用了beginArray()就要使用endArray()結尾。

開源庫Gson使用

嘗試完了繁瑣的Android自帶的Json解析類,現(xiàn)在就來看看第三方開源庫Gson的威力吧!

  • 導入依賴

dependencies { implementation 'com.google.code.gson:gson:2.8.5' }

  • Gson的基本使用

    Gson對象的創(chuàng)建

    有兩種方式來創(chuàng)建Gson對象。

    第一種是直接new一個對象: Gson gson = new Gson();

    第二種是利用GsonBuilder來創(chuàng)建,可以進行多項配置:Gson gson = new GsonBuilder().create();

    Gson的反序列化

    Gson最為重要的兩個方法分別是toJson()和fromJson()。toJson()用來序列化,fromJson()用來反序列化。

    1 簡單Json數(shù)組解析:

    Json數(shù)據(jù):

[ "Happy New Year 2019 : Happy New Year Happy New Year 2019",
  "Karma's black iron prison",
  "Hong Kong gov't bid to redevelop Fanling course 'absurd' says golf alliance convener"
]

Json數(shù)組轉化為字符串數(shù)組:

       Gson gson = new Gson();
        String json_t2 = null;
        try {
            json_t2 = jsonFromLocal(R.raw.json_t2);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String[] strings = gson.fromJson(json_t2, String[].class);
        for (String st : list) {
            Log.i(TAG, st);
        }

Json數(shù)組轉化為字符串List:

         Gson gson = new Gson();
        String json_t2 = null;
        try {
            json_t1 = jsonFromLocal(R.raw.json_t2);
        } catch (IOException e) {
            e.printStackTrace();
        }
        List<String> list = gson.fromJson(json_t2,new TypeToken<List<String>>(){}.getType());
        for (String st : list) {
            Log.i(TAG, st);
        }

Gson中的泛型表示:

在解析Json數(shù)組時,一般使用兩種方法,一個是數(shù)組,一個是List。而因為List對于增刪操作比較方便,一般選用List。
但對于List來說,上面代碼中的 String[].class ,不能直接改為List<String>.class。因為對于Java來說 List<String>List<Item>這兩個字節(jié)碼文件只有一個,就是 List.class,這就是Java泛型使用時要注意的泛型擦除問題。

為了解決上述問題,Gson提供TypeToken來實現(xiàn)對泛型的支持。TypeToken 這個類幫助我們捕獲(capture)像 List 這樣的泛型信息。Java編譯器會把捕獲到的泛型信息編譯到這個匿名內(nèi)部類里,然后在運行時就可以被getType()方法用反射的 API 提取到。也就是將泛型T轉成 .class

2 稍復雜Json數(shù)據(jù)解析:

Json數(shù)據(jù),目的是為了拿到totol數(shù)據(jù):

{
  "photo": [
    {
      "page": 1,
      "pages": 10,
      "perpage": 100,
      "total": "1000"
    },
    {
      "page": 2,
      "pages": 20,
      "perpage": 200,
      "total": "1000"
    },
    {
      "page": 3,
      "pages": 30,
      "perpage": 300,
      "total": "1000"
    }
  ]
}

第一步:根據(jù)Json數(shù)據(jù)建立簡單Java類。可以使用Android Studio平臺中的GsonFormat插件來快速構建。

 public class JsonT1 {

    public List<PhotoBean> photo;

    public static class PhotoBean {
        /**
         * page : 1
         * pages : 10
         * perpage : 100
         * total : 1000
         */

        public int page;
        public int pages;
        public int perpage;
        public String total;
    }
}

第二步:解析Json數(shù)據(jù)。這里有兩種方式,第一種是利用JsonParse類解析,有點類似于Android自帶的JsonObject和JsonArray解析方式,得要扣需要的key。下面來看一下實現(xiàn)代碼。

JsonObject jsonObject = new JsonParser().parse(json_t1).getAsJsonObject();
        JsonArray jsonArray = jsonObject.getAsJsonArray("photo");
        List<JsonT1.PhotoBean> jsonT1List = new ArrayList<>();
        for (JsonElement element : jsonArray) {
            JsonT1.PhotoBean jsonT1 = gson.fromJson(element, new TypeToken<JsonT1.PhotoBean>() {
            }.getType());
            jsonT1List.add(jsonT1);
        }
    Log.i(TAG, jsonT1List.get(0).total);

第二種是直接將Json數(shù)據(jù)解析為類對象,在得到對象之后再從對象內(nèi)取出需要的List。

JsonT1 t1 = gson.fromJson(json_t1, JsonT1.class);
        List<JsonT1.PhotoBean> photoBeanList = t1.photo;
        Log.i(TAG,photoBeanList.get(0).total);

比較而言第二種方式更簡潔一點,而且遇到更復雜一點的Json數(shù)據(jù)也可以使用。

3復雜Json數(shù)據(jù)解析

當遇到這種Json數(shù)據(jù)時:

{
  "photos": {
    "page": 1,
    "pages": 10,
    "perpage": 100,
    "total": "1000",
    "photo": [
      {
        "id": "31624984467",
        "owner": "155140314@N03",
        "secret": "efa8606dc9",
        "server": "4852",
        "farm": 5,
        "title": "Happy New Year 2019 : Happy New Year Happy New Year 2019",
        "ispublic": 1,
        "isfriend": 0,
        "isfamily": 0,
        "url_s": "https:\/\/farm5.staticflickr.com\/4852\/31624984467_efa8606dc9_m.jpg",
        "height_s": "240",
        "width_s": "240"
      },
      {
        "id": "31624992207",
        "owner": "163032290@N04",
        "secret": "a3196f29b6",
        "server": "4844",
        "farm": 5,
        "title": "",
        "ispublic": 1,
        "isfriend": 0,
        "isfamily": 0,
        "url_s": "https:\/\/farm5.staticflickr.com\/4844\/31624992207_a3196f29b6_m.jpg",
        "height_s": "135",
        "width_s": "240"
      },
  "stat": "ok"
}

就到了JsonReader大顯身手的地方了。使用這種流式處理方式再復雜的數(shù)據(jù)也能搞定。JsonReader是不是有點眼熟?沒錯,它和之前提到的Android自帶的JsonReader處理方式幾乎沒有區(qū)別,連名字都一樣。

JsonReader.png
  • Gson注解

    Gson的常用注解有@Expose@SerializedName@Since@Until@JsonAdapter

    1 @Expose注解

    本注解用于指定某個屬性是否進行序列化或反序列化,搭配GsonBuilder使用,源碼如下。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Expose {
  public boolean serialize() default true;
  public boolean deserialize() default true;
}

包含兩個屬性serializedeserializeserialize 用于指定是否進行序列化,deserialize用于指定是否進行反序列化,默認為true。

舉個栗子:

@Expose(serialize = false, deserialize = true)//可以反序列化,不可以序列化
 public int page;

使用:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

2 @SerializedName注解

重命名指定的屬性,源碼如下。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SerializedName {
  String value();
}

使用:

@SerializedName("pagesTotal")
 public int pages;

3 @Since@Until注解

@Expose注解一樣,用來指定某一屬性是否可以序列化或反序列化。@Since@Until 都包含一個 Double 屬性值,用于設置版本號。Since 的意思是“自……開始”,Until 的意思是“到……為止”。當版本( GsonBuilder 設置的版本) 大于或等于 Since 屬性值或小于 Until 屬性值時字段會進行序列化和反序列化操作,而沒有聲明的字段都會加入序列化和反序列操作。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Until {
  double value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Since {
 double value();
}

使用:

@Since(1.1)
 public int perpage;
@Until(1.8)
 public String total;
Gson gson = new GsonBuilder().setVersion(1.3).create();

4 @JsonAdapter注解

自定義序列化和反序列化,可以修飾類和字段。

首先需要自定義一個TypeAdapter的子類來接管目標類的序列化和反序列化過程,并且實現(xiàn)write和read方法。

public class SelfTypeAdapter extends TypeAdapter<JsonT1> {
    @Override
    public void write(JsonWriter out, JsonT1 value) throws IOException {

    }

    @Override
    public JsonT1 read(JsonReader in) throws IOException {
        JsonT1 t1 = new JsonT1();
        in.beginObject();
        //.....解析操作
        in.endObject();
        return t1;
    }
}

接著是注冊自定義的SelfTypeAdapter類:

Gson gs = new GsonBuilder().registerTypeAdapter(JsonT1.class, new SelfTypeAdapter()).create();
  • 其他Gson配置

1 格式化輸出

Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .create();

2 日期時間格式化輸出

Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .setDateFormat("yyyy-MM-dd HH:mm:ss:SSS")//日期時間格式

3 Null值輸出

Gson gson = new GsonBuilder()
                .serializeNulls() 
                .create();

參考:
https://juejin.im/post/59e5663f51882546b15b92f0
http://www.lxweimin.com/p/0444693c2639
http://www.lxweimin.com/p/886f7b7fca7d
http://www.runoob.com/json/js-json-arrays.html

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內(nèi)容

  • 1.概述2.Gson的目標3.Gson的性能和擴展性4.Gson的使用者5.如何使用Gson 通過Maven來使用...
    人失格閱讀 14,310評論 2 18
  • 為了更好的學習Gson,特將Gson User Guide翻譯如下。由于本人英文水平有限,如有錯誤,還請指正,謝謝...
    WeberLisper閱讀 6,893評論 0 6
  • JSON的特點: 1、JSON比XML的數(shù)據(jù)傳遞的有效性高;2、JSON完全獨立于編程語言;3、JSON的本質(zhì)是具...
    程序員之路閱讀 3,531評論 0 3
  • 概況 Gson是一個Java庫,它可以用來把Java對象轉換為JSON表達式,也可以反過來把JSON字符串轉換成與...
    木豚閱讀 6,828評論 0 2
  • 城市的喧囂,校園的浪漫,市井的嘈雜,街頭的繁華,似乎在我眼里都沒有那么如愿的美好和怡然。因為身邊沒有你,所以...
    焉然南山閱讀 151評論 0 1