Android下的Json学习指南

2019-02-28  本文已影响0人  two2twoooo

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

Android中的Json解析

利用Android自带的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等方法)

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

开源库Gson使用

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

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

[ "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
@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();

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

上一篇 下一篇

猜你喜欢

热点阅读