Android:Gson解析 - 从简单数据到复杂数据

Json是一种轻量级的数据交换语言,以文字为基础,且易于让人阅读和编写,同时也易于机器解析和生成,因而在客户端与服务器交互中得到广泛应用。但Json自带的解析类用起来却差强人意,所以市面上因运而生了很多Json转换利器,本文主要介绍其中之一Gson。Gson是google发布的library,主要为了方便将Java对象序列化Serialization至轻量化的封包格式JSON,提供了很多方便快捷的方法。

通过gradle导入包

登录网站MVNRepository,在搜索框中搜索gson,即可出现各种不同的版本,选择gradle下需要的版本,并复制到gradle文件中即可。

Gson基本用法

Gson提供了两个方法直接用于解析和生成方法,二者都有重载方法:

  • fromJson():实现反序列化
  • toJson():实现序列化

基本数据类型的生成

Gson gson = new Gson();
String jsonNumber = gson.toJson(100); // 100
String jsonBoolean = gson.toJson(false); // false
String jsonString = gson.toJson("String"); //"String"

POJO类的生成与解析

对于普通Java类:

public class User {
   public String name; 
   public int age; 
}
生成JSON
Gson gson = new Gson();
User user = new User("Sunny",24);
String jsonObject = gson.toJson(user); // {"name":"Sunny","age":24}
解析JSON
Gson gson = new Gson();
String jsonString = "{\"name\":\"sunny\",\"age\":24}";
User user = gson.fromJson(jsonString, User.class);

注:POJO(Plain Old Java Object)表示普通Java对象,不是JavaBean,EntityBean或者SessionBean。POJO不担当任何特殊的角色,也不实现任何特殊的Java框架的接口。

Gson中的泛型

当我们解析Json数组时,一般有两种方式:使用数组,使用List。而List对于增删都比较方便,所以实际用List较多

数组
String[] strings = gson.fromJson(jsonArray, String[].class);

但对于List将上面代码中的 String[].class ,不能直接改为List<String>.class。对于Java来说 List<String>List<User>这两个字节码文件只有一个,就是 List.class,这就是Java泛型使用时要注意的泛型擦除问题。

List

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

List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());

注:TypeToken
的构造方法是protected
修饰的,所以上面才会写成new TypeToken<List<String>>() {}.getType()
而不是new TypeToken<List<String>>().getType()

@Expose、@SerializeName注解的使用

Json解析的字段和值得名称和类型是一一对应的,但有时前端和后台使用的语言不同,二者语言规范风格不同,命名时常常会出现不和谐的地方。这时 @Expose @SerializeName 两个注解就要发挥他们的作用啦

  • @Expose注解:区分实体中不想被序列化的属性,其自身包含两个属性deserialize(反序列化)和serialize(序列化),默认都为true
  • @SerializeName注解:定义属性序列化后的名称
    使用 new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();创建Gson对象,没有@Expose注释的属性将不会被序列化.。另外想要不序列化某个属性,也可以使用transient。

Gson在序列化和反序列化时需要使用反射,说到反射就想到注解,一般各类库都将注解放到annotations包下,打开源码在com.google.gson包下有一个annotations类,其中有一个SerializedName的注解类,这就是我们用到的。

Gson解析复杂Json数据

首先,要知道两个类:
1、JsonParse
从名称我们就可以看出,这是一个解析类。没错,它可以把 JSON 数据分别通过getAsJsonObjectgetAsJsonArray解析成JsonObjectJsonArray。这跟普通的解析 JSON 差不多。
2、JsonElement
是一个抽象类,代表 JSON 串中的某一个元素,可以是JsonObject/JsonArray/JsonPrimitive/...中的任何一种元素。

没有数据头的纯数组

JSON里面只有一个数组,而数组中没有名字:

[
  {
    "name": "zhangsan",
    "age": "10",
    "phone": "11111",
    "email": "11111@11.com"
  },
  {
    "name": "lisi",
    "age": "20",
    "phone": "22222",
    "email": "22222@22.com"
  },
  ...
]

开始解析,首先定义一个用户类:

public class UserBean {
    //变量名跟JSON数据的字段名需要一致
    private String name ;
    private String age;
    private String phone;
    private String email;
    ...
}

方法一:Gson可以直接解析成一个List

List<UserBean> userList = gson.fromJson(jsonStr, new TypeToken<List<UserBean>>(){}.getType());

方法二:传统老实方法

    //Json的解析类对象
    JsonParser parser = new JsonParser();
    //将JSON的String 转成一个JsonArray对象
    JsonArray jsonArray = parser.parse(strByJson).getAsJsonArray();

    Gson gson = new Gson();
    ArrayList<UserBean> userBeanList = new ArrayList<>();

    //加强for循环遍历JsonArray
    for (JsonElement user : jsonArray) {
        //使用GSON,直接转成Bean对象
        UserBean userBean = gson.fromJson(user, UserBean.class);
        userBeanList.add(userBean);
    }
有数据头的纯数组数据
{
  "user": [
    {
      "name": "zhangsan",
      "age": "10",
      "phone": "11111",
      "email": "11111@11.com"
    },
    {
      "name": "lisi",
      "age": "20",
      "phone": "22222",
      "email": "22222@22.com"
    },
    ...
  ]
}

开始解析:

    //先转JsonObject
    JsonObject jsonObject = new JsonParser().parse(strByJson).getAsJsonObject();
    //再转JsonArray 加上数据头
    JsonArray jsonArray = jsonObject.getAsJsonArray("user");

    Gson gson = new Gson();
    ArrayList<UserBean> userBeanList = new ArrayList<>();

    //循环遍历
    for (JsonElement user : jsonArray) {
        //通过反射 得到UserBean.class
        UserBean userBean = gson.fromJson(user, new TypeToken<UserBean>() {}.getType());
        userBeanList.add(userBean);
    }
有数据头的复杂数据
{
  "code": 200,
  "msg": "OK",
  "user": [
    {
      "name": "zhangsan",
      "age": "10",
      "phone": "11111",
      "email": "11111@11.com"
    },
    {
      "name": "lisi",
      "age": "20",
      "phone": "22222",
      "email": "22222@22.com"
    },
    ...
  ]
}

开始解析:
第一步:根据 JSON 建立 Bean ,注意这里的 Bean 是返回所有字段,因为 GSON 能直接解析成 List ,同时把 get/set 省略。

public class ResultBean {
    //注意变量名与字段名一致
    private int code;
    private String msg;
    private List<UserBean> user;

    public class UserBean{
        private String name ;
        private String age;
        private String phone;
        private String email;
        ...
    }
    ...
}

注意:这个类中有一个UserBean,也可以定义在外面,可以作为JsonArray解析后存入List中的对象。

   //GSON直接解析成对象
    ResultBean resultBean = new Gson().fromJson(strByJson,ResultBean.class);
    //对象中拿到集合
    List<ResultBean.UserBean> userBeanList = resultBean.getUser();

另外,变量名与字段名不一致也可以,因为Json中字段通常为下划线方式,而Java中字段通常为驼峰方式,故需要能够转换对应,而Gson也有相关功能,例如:

public class UserBean {
      @SerializeName("user_name")
      private String userName;
      @SerializeName("user_age")
      private String userAge;
}

工具推荐

jsonschema2pojo:根据JSON结构产生POJO对象

参考资料

Gson User Guide
通透Gson@Expose注解、@SerializedName、解析json数据
Android:用GSON 五招之内搞定任何JSON数组
你真的会用Gson吗?Gson使用指南(一)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容