美文网首页
Android下的Json学习指南

Android下的Json学习指南

作者: two2twoooo | 来源:发表于2019-02-28 21:12 被阅读0次

    Json基础知识

    • Json是什么

      Json是JavaScript Object Notation(JavaScript对象表示法)的缩写,是一种轻量级的文本数据交换格式,是用来存储和交换文本信息的语法。

      它独立于语言且具有自我描述性,更容易理解。虽然使用Javascript语法来描述数据对象,但很多编程语言都支持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. 数据在名称/值对{ key:value }中
    2. 数据由逗号分隔
    3. 大括号保存对象
    4. 中括号保存数组

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

    1. 数字(整数或浮点数)
    2. 字符串(在双引号中)
    3. 逻辑值(true 或 false)
    4. 数组(在中括号中)
    5. 对象(在大括号中)
    6. null
    • Json对象

      JSON 对象在大括号({ })中书写,对象可以包含多个名称/值对。名称/值对中使用冒号(:)分割,每个名称/值对对使用逗号(,)分割。

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

    • Json数组

      JSON 数组在中括号中书写,数组可包含多个对象,也可只是合法的基本数据类型:

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

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

    Android中的Json解析

    利用Android自带的Json解析类

    • 使用JSONObject,JSONArray来解析:

      JSONObject类:生成Json对象,可以完成Json字符串与Java对象的相互转换。

      JSONArray类:生成Json数组,可以完成Json字符串与Java集合或对象的相互转换。

      JSONStringer类:Json文本构建类,这个类可以帮助快速和便捷的创建JSON text, 每个JSONStringer实体只能对应创建一个JSON text。

      JSONException类:Json异常。

      使用步骤介绍:

      1. 获取Json字符串。一般是从网络获取,为了方便这里直接从本地获取。
      2. 使用JSONObject类,利用Json字符串生成JSONObject对象。
      3. 使用getJSONObject,getJSONArray等方法一个个的将Json字符串分解,再从中抠出所需要的属性。

    举个栗子:

    josn数据为:

    { "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数据

    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内的对象数组photo
        JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
        for (int i = 0; i < 3; i++) {
    //再依据具体的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文档的解析,使用深度优先遍历,寻找到需要的标记然后消耗掉它。对象的解析从beginObject()开始,到endObject()结束。数组的解析从beginArray()开始,到endArray()结束。

      下面我们来看一看吧,json数据依旧是之前的!

          JsonReader reader = new JsonReader(new StringReader(json));//获取reader对象
          try {
              reader.beginObject();//从Json流中消耗一个标记,开始解析对象
              while (reader.hasNext()) {//循环判断当前数组或者对象是否有下一个元素
                  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();//开始数组解析
          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();//结束数组解析
      }`
      
      

    注意:
    如果使用了nextName(),而没有使用next **()( **可以是String,Int,Long等)方法或者skipValue(),这时候就会触发异常,这一组方法需要配套使用。同理使用了beginObject()就要使用endObject()结尾,使用了beginArray()就要使用endArray()结尾。

    开源库Gson使用

    尝试完了繁琐的Android自带的Json解析类,现在就来看看第三方开源库Gson的威力吧!

    • 导入依赖

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

    • Gson的基本使用

      Gson对象的创建

      有两种方式来创建Gson对象。

      第一种是直接new一个对象: Gson gson = new Gson();

      第二种是利用GsonBuilder来创建,可以进行多项配置:Gson gson = new GsonBuilder().create();

      Gson的反序列化

      Gson最为重要的两个方法分别是toJson()和fromJson()。toJson()用来序列化,fromJson()用来反序列化。

      1 简单Json数组解析:

      Json数据:

    [ "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数组转化为字符串数组:

           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数组转化为字符串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数组时,一般使用两种方法,一个是数组,一个是List。而因为List对于增删操作比较方便,一般选用List。
    但对于List来说,上面代码中的 String[].class ,不能直接改为List<String>.class。因为对于Java来说 List<String>List<Item>这两个字节码文件只有一个,就是 List.class,这就是Java泛型使用时要注意的泛型擦除问题。

    为了解决上述问题,Gson提供TypeToken来实现对泛型的支持。TypeToken 这个类帮助我们捕获(capture)像 List 这样的泛型信息。Java编译器会把捕获到的泛型信息编译到这个匿名内部类里,然后在运行时就可以被getType()方法用反射的 API 提取到。也就是将泛型T转成 .class

    2 稍复杂Json数据解析:

    Json数据,目的是为了拿到totol数据:

    {
      "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"
        }
      ]
    }
    

    第一步:根据Json数据建立简单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数据。这里有两种方式,第一种是利用JsonParse类解析,有点类似于Android自带的JsonObject和JsonArray解析方式,得要扣需要的key。下面来看一下实现代码。

    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数据解析为类对象,在得到对象之后再从对象内取出需要的List。

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

    比较而言第二种方式更简洁一点,而且遇到更复杂一点的Json数据也可以使用。

    3复杂Json数据解析

    当遇到这种Json数据时:

    {
      "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大显身手的地方了。使用这种流式处理方式再复杂的数据也能搞定。JsonReader是不是有点眼熟?没错,它和之前提到的Android自带的JsonReader处理方式几乎没有区别,连名字都一样。

    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的子类来接管目标类的序列化和反序列化过程,并且实现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
    https://www.jianshu.com/p/0444693c2639
    https://www.jianshu.com/p/886f7b7fca7d
    http://www.runoob.com/json/js-json-arrays.html

    相关文章

      网友评论

          本文标题:Android下的Json学习指南

          本文链接:https://www.haomeiwen.com/subject/kwrkuqtx.html