Android下的Json学习指南

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 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 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 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 list = gson.fromJson(json_t2,new TypeToken>(){}.getType());
        for (String st : list) {
            Log.i(TAG, st);
        }

Gson中的泛型表示:

在解析Json数组时,一般使用两种方法,一个是数组,一个是List。而因为List对于增删操作比较方便,一般选用List。
但对于List来说,上面代码中的 String[].class ,不能直接改为List.class。因为对于Java来说 ListList这两个字节码文件只有一个,就是 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 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 jsonT1List = new ArrayList<>();
        for (JsonElement element : jsonArray) {
            JsonT1.PhotoBean jsonT1 = gson.fromJson(element, new TypeToken() {
            }.getType());
            jsonT1List.add(jsonT1);
        }
    Log.i(TAG, jsonT1List.get(0).total);

第二种是直接将Json数据解析为类对象,在得到对象之后再从对象内取出需要的List。

JsonT1 t1 = gson.fromJson(json_t1, JsonT1.class);
        List 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 {
    @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学习指南)