[From] http://www.open-open.com/lib/view/open1472632967912.html

写在前面

关于GSON的入门级使用,这里就不提了,如有需要可以看这篇博文 《Google Gson的使用方法,实现Json结构的相互转换》 ,写的很好,通俗易懂。

我为什么写这篇文章呢?因为前几晚跟好友 xiasuhuei321 探讨了一下GSON解析复杂的JSON的时候,能不能只解析源数据中的数组,甚至只解析数组的某一部分。探讨了二十分钟,得出结论:没用过,不知道。

所以今天特地研究了一下,发现真的So Easy!之前想复杂了,学习的过程中,发现有五种方式分别搞定不同情况的JSON数组,也就是今天说的五大招!

在介绍之前先来个约定,比如下面的这个JSON:

  1. "muser": [
  2. {
  3. "name": "zhangsan",
  4. "age": "10",
  5. "phone": "11111",
  6. "email": "11111@11.com"
  7. },
  8. ...
  9. ]
  • 这里的 “muser” ,也就是数组的名称,称它为数据头,防止跟里面的 字段 有歧义;
  • 如果没有数据头,那就叫它纯数据,或者纯数组数据;
  • 代码中用到的 JsonArray/JsonObject 等熟悉的类全部来自 GSON 。

开始过招吧!

第一招 A

没有数据头的纯数组JSON如何解析?

根据约定,也就是这个 JSON 里面只有一个数组(JsonArray),而且这个数组没有名字,比如像下面这样的:

  1. [
  2. {
  3. "name": "zhangsan",
  4. "age": "10",
  5. "phone": "11111",
  6. "email": "11111@11.com"
  7. },
  8. {
  9. "name": "lisi",
  10. "age": "20",
  11. "phone": "22222",
  12. "email": "22222@22.com"
  13. },
  14. ...
  15. ]

这里其实是最简单的一种 JSON 数组格式,强大的 GSON 可以直接解析成一个 List 。但在这里我先不直接解析,就用比较老实的方法去解析,因为需要引出两个东西。

首先我们需要建立一个Bean对象,注意变量名要跟字段名称一致,没什么好说的:

  1. public class UserBean {
  2. //变量名跟JSON数据的字段名需要一致
  3. private String name ;
  4. private String age;
  5. private String phone;
  6. private String email;
  7. ...
  8. }

下面这是解析过程,先看代码:

  1. /**
  2. * 解析没有数据头的纯数组
  3. */
  4. private void parseNoHeaderJArray() {
  5. //拿到本地JSON 并转成String
  6. String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_1);
  7. //Json的解析类对象
  8. JsonParser parser = new JsonParser();
  9. //将JSON的String 转成一个JsonArray对象
  10. JsonArray jsonArray = parser.parse(strByJson).getAsJsonArray();
  11. Gson gson = new Gson();
  12. ArrayList<UserBean> userBeanList = new ArrayList<>();
  13. //加强for循环遍历JsonArray
  14. for (JsonElement user : jsonArray) {
  15. //使用GSON,直接转成Bean对象
  16. UserBean userBean = gson.fromJson(user, UserBean.class);
  17. userBeanList.add(userBean);
  18. }
  19. mainLView.setAdapter(new UserAdapter(this, userBeanList));
  20. }

从代码中可以看出解析的步骤如下:

  • 无论 JSON 来自本地还是网络获取,都要先将 JSON 转成 String ;
  • 需要一个 JSON 解析类对象将JSON的字符串转成 JsonArray ,前提是我们知道 JSON 中只有纯数组;
  • 循环遍历 JsonArray ,并用 GSON 解析成相应的对象。

代码本身不难,容易看懂,但前面说到,这里我故意这样写,因为需要说两个东西:

1、JsonParse

从名称我们就可以看出,这是一个解析类。没错,它可以把 JSON 数据分别通过 getAsJsonObject 和 getAsJsonArray 解析成 JsonObject 和 JsonArray 。这跟普通的解析 JSON 差不多,不展开说。

2、JsonElement

这个类我是第一次见,它是一个抽象类,代表 JSON 串中的某一个元素,可以是 JsonObject/JsonArray/JsonPrimitive/… 中的任何一种元素。

所以在上面的代码中,我们可以看到它能把 JsonArray 中的每一个元素转成 JsonObject ,甚至说它本身就是 JsonObject 。

好了,就为了说这两个东西。记住,后面将会用到。

来看一下运行的图吧,很简单的东西,后面的二三都是这样的效果,就不重复贴图了:

第二招 Q

有数据头的纯数组数据该怎么解析?

内容跟上面的 JSON 一模一样,只不过加了一个名称 “muser” ,也就是约定好的 数据头 :

  1. {
  2. "muser": [
  3. {
  4. "name": "zhangsan",
  5. "age": "10",
  6. "phone": "11111",
  7. "email": "11111@11.com"
  8. },
  9. {
  10. "name": "lisi",
  11. "age": "20",
  12. "phone": "22222",
  13. "email": "22222@22.com"
  14. },
  15. ...
  16. ]
  17. }

有人说,这还不简单,在第一招中的 getAsJsonArray 加一个字符串就是咯,就像这样:

  1. JsonArray jsonArray = parser.parse(strByJson).getAsJsonArray("muser");

思路是对的,但是不要忘了,数组装在一个 { } 括起来的 JsonObject 里。还记得上面的 JsonParse 么,它的 getAsJsonObject 可以做到这点,所以代码就是这样啦,很简单就不再解释了:

  1. /**
  2. * 解析有数据头的纯数组
  3. */
  4. private void parseHaveHeaderJArray() {
  5. //拿到本地JSON 并转成String
  6. String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_2);
  7. //先转JsonObject
  8. JsonObject jsonObject = new JsonParser().parse(strByJson).getAsJsonObject();
  9. //再转JsonArray 加上数据头
  10. JsonArray jsonArray = jsonObject.getAsJsonArray("muser");
  11. Gson gson = new Gson();
  12. ArrayList<UserBean> userBeanList = new ArrayList<>();
  13. //循环遍历
  14. for (JsonElement user : jsonArray) {
  15. //通过反射 得到UserBean.class
  16. UserBean userBean = gson.fromJson(user, new TypeToken<UserBean>() {}.getType());
  17. userBeanList.add(userBean);
  18. }
  19. mainLView.setAdapter(new UserAdapter(this, userBeanList));
  20. }

注意,这里又引出了一个东西: TypeToken ,它是什么呢?

3、TypeToken

这个东西很有意思,本来我不知道到是干嘛的,看了看源码,看不懂。后来无意发现它所在的包:

  1. import com.google.gson.reflect.TypeToken;

哎哟我去, reflect 这不是反射么,一下子就明白了。没错,它其实是一个匿名内部类,看一下官方解释:

GSON 提供了 TypeToken 这个类来帮助我们捕获(capture)像 List 这样的泛型信息。Java编译器会把捕获到的泛型信息编译到这个匿名内部类里,然后在运行时就可以被 getType() 方法用反射的 API 提取到。

解释的很官方,实际上就是一句 通俗但不严谨 的话,它将泛型 T 转成 .class 。比如上面的 TypeToken 经过 getType() 后就是 UserBean.class 。

好了,说到这里基本铺垫就完成了,再次强调一下:

对于上面的 JSON 完全可以直接通过 GSON 转成 List ,不用这么麻烦,我只是为了引出3个小知识。

第三招 W

有数据头的复杂数据该如何解析呢?

简单的说完了,铺垫也铺完了,来看一看复杂的吧:

  1. {
  2. "code": 200,
  3. "msg": "OK",
  4. "muser": [
  5. {
  6. "name": "zhangsan",
  7. "age": "10",
  8. "phone": "11111",
  9. "email": "11111@11.com"
  10. },
  11. {
  12. "name": "lisi",
  13. "age": "20",
  14. "phone": "22222",
  15. "email": "22222@22.com"
  16. },
  17. ...
  18. ]
  19. }

这里就不再是纯数组数据了,还有两个凑数的不知道干嘛用的字段,这里也有数据头,之前用的是笨方法,现在来真正见识一下GSON的威力吧。

第一步根据 JSON 建立 Bean ,注意这里的 Bean 是返回所有字段,因为 GSON 能直接解析成 List ,所以 Bean 是下面这样的,同样把占地方的 get/set 省略:

  1. /**
  2. * Created by xiarui on 2016/8/30.
  3. * 返回所有结果的Bean
  4. */
  5. public class ResultBean {
  6. //注意变量名与字段名一致
  7. private int code;
  8. private String msg;
  9. private List<UserBean> muser;
  10. public class UserBean{
  11. private String name ;
  12. private String age;
  13. private String phone;
  14. private String email;
  15. ...
  16. }
  17. ...
  18. }

注意,这个 ResultBean 里面有一个 UserBean 。 它虽然跟上面第一第二招虽然内容一样,但是作用不一样,这是作为 JsonArray 解析后存入 List 中的对象。

算了,有点拗口,直接上代码吧:

  1. /**
  2. * 有消息头 复杂数据 常规方式
  3. */
  4. private void parseComplexJArrayByCommon() {
  5. //拿到Json字符串
  6. String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_3);
  7. //GSON直接解析成对象
  8. ResultBean resultBean = new Gson().fromJson(strByJson,ResultBean.class);
  9. //对象中拿到集合
  10. List<ResultBean.UserBean> userBeanList = resultBean.getMuser();
  11. //展示到UI中
  12. mainLView.setAdapter(new ResultAdapter(this, userBeanList));
  13. }

没错,就是这么四句话搞定第一二招的内容。看出GSON的强大了吧,当然如果有人想不开只写一句话的话:

  1. mainLView.setAdapter(new ResultAdapter(this,new Gson().fromJson(JsonToStringUtil.getStringByJson(this,R.raw.juser_3),ResultBean.class).getMuser()));

我也是没意见的,不过请对自己好一点,谢谢。

第四招 E

只想解析复杂JSON中的数组或数组中的某部分内容怎么办?

好了,来到重点了,这也是跟好友 xiasuhuei321 没有讨论出来的情况。

还是上面的JSON数据,这里为了篇幅就不贴重复代码了,假如我只想取 “muser” 这个数组中的年龄(age)大于30岁的怎么办?

OK,当然可以先全部解析,再从 List 中取。那假如我有一万条数据呢?全部解析不是很麻烦呢?

所以一个思路就是第一二招中说的: 遍历!

OK,你会问先遍历还不是要读一万条,是的,还是要读一万条,但是假如我要把这些存入数据库呢?假如一万条数据中只有一条符合条件,难道我先存一万条,再从数据库中查询么?

当然这种情况是极端情况,但也说明了一个问题,不能所有情况下都先全部解析,假如有一万个字段,Bean还得写多长…可怕。

现在来说一下完整的思路,也是我学习中思考的过程:

  • 第一点肯定就是刚才提到的遍历,这个很好理解,所以我们先要取这一个数组(JsonArray),那么如何取呢?还记得之前提到的 JsonParse 么,它的 getAsJsonArray() 可以传入 数据头 拿到数组,当然不要忘了最外面一层是个 JsonObject 。

    1. //最外层
    2. JsonObject jsonObject = new JsonParser().parse(strByJson).getAsJsonObject();
    3. //需要遍历的数组
    4. JsonArray jsonArray = jsonObject.getAsJsonArray("muser");
  • 拿到数组以后,我们就可以遍历了,经过第一二招的洗礼,相信在遍历上,应该没什么问题了,使用的还是之前提到的 JsonElement 。
    1. //循环遍历数组
    2. for (JsonElement user : jsonArray) {
    3. UserBean userBean = new Gson().fromJson(user, new TypeToken<UserBean>() {}.getType());
    4. //根据条件过滤
    5. if (Integer.parseInt(userBean.getAge()) > 30) {
    6. userBeanList.add(userBean);
    7. }
    8. }
  • 上面的代码很简单,也用到了之前提到的 TypeToken ,什么意思就不用解释了吧。

好了,完整的代码如下:

  1. /**
  2. * 有数据头 复杂数据 截取方式
  3. */
  4. private void parseComplexJArrayByDirect() {
  5. //拿到JSON字符串
  6. String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_3);
  7. List<UserBean> userBeanList = new ArrayList<>();
  8. //拿到数组
  9. JsonObject jsonObject = new JsonParser().parse(strByJson).getAsJsonObject();
  10. JsonArray jsonArray = jsonObject.getAsJsonArray("muser");
  11. //循环遍历数组
  12. for (JsonElement user : jsonArray) {
  13. UserBean userBean = new Gson().fromJson(user, new TypeToken<UserBean>() {
  14. }.getType());
  15. //根据条件过滤
  16. if (Integer.parseInt(userBean.getAge()) > 30) {
  17. userBeanList.add(userBean);
  18. }
  19. }
  20. mainLView.setAdapter(new UserAdapter(this, userBeanList));
  21. }

运行的结果图如下:

可以看到,现在我们做到了只取 JSON 数据中数组中某一部分了。那么扩展一下,只取 JSON 数据中的某一个数组中的某一个字段呢?当然可以实现,不过还是留给大家自己思考吧,当然下面反人类的第五招也是可以解决这个问题的。

第五招 R

如果一个 JSON 数据很很很复杂怎么解析?

什么叫做复杂,这里我简单写了个比较复杂的,有数据头,一层嵌套一层,我还没有写数组呢:

  1. {
  2. "group": {
  3. "user": {
  4. "name": "张三",
  5. "age": "10",
  6. "phone": "11111",
  7. "email": "11111@11.com"
  8. },
  9. "info": {
  10. "address": "北京",
  11. "work": "Android Dev",
  12. "pay": "10K",
  13. "motto": "先定一个小目标,比如我先赚一个亿"
  14. }
  15. }
  16. }

三种方式解析:

  • 第三招,全部解析出来;
  • 第四招,要什么解析什么;
  • 第五招,反人类的 JsonReader 。

至于为什么反人类,不好说。大家看代码就知道了,代码很简单,跟 XML 的解析差不多,是根据节点来的,至于怎么用,还是那句话直接看代码吧,确实处理起来逻辑清晰,但是代码量上,真的不敢恭维。

只贴代码不作解释,如想详细了解,看文末链接。

  1. /**
  2. * 通过JsonReader的方式去解析
  3. */
  4. private void parseComplexJArrayByReader() throws IOException {
  5. String strByJson = JsonToStringUtil.getStringByJson(this, R.raw.juser_4);
  6. JsonReader reader = new JsonReader(new StringReader(strByJson));
  7. try {
  8. reader.beginObject();
  9. String tagName = reader.nextName();
  10. if (tagName.equals("group")) {
  11. //读group这个节点
  12. readGroup(reader);
  13. }
  14. reader.endObject();
  15. } finally {
  16. reader.close();
  17. }
  18. }
  19. /**
  20. * 读group这个节点
  21. *
  22. * @param reader JsonReader
  23. */
  24. private void readGroup(JsonReader reader) throws IOException {
  25. reader.beginObject();
  26. while (reader.hasNext()) {
  27. String tagName = reader.nextName();
  28. if (tagName.equals("user")) {
  29. readUser(reader);
  30. } else if (tagName.equals("info")) {
  31. readInfo(reader);
  32. }
  33. }
  34. reader.endObject();
  35. }
  36. /**
  37. * 读用户基本消息 user节点
  38. *
  39. * @param reader JsonReader
  40. */
  41. private void readUser(JsonReader reader) throws IOException {
  42. reader.beginObject();
  43. while (reader.hasNext()) {
  44. String tag = reader.nextName();
  45. if (tag.equals("name")) {
  46. String name = reader.nextString();
  47. nameText.setText(name);
  48. } else if (tag.equals("age")) {
  49. String age = reader.nextString();
  50. ageText.setText(age);
  51. }
  52. ...
  53. else {
  54. reader.skipValue();//忽略
  55. }
  56. }
  57. reader.endObject();
  58. }
  59. /**
  60. * 读用户其他消息 info节点
  61. *
  62. * @param reader JsonReader
  63. */
  64. private void readInfo(JsonReader reader) throws IOException {
  65. reader.beginObject();
  66. while (reader.hasNext()) {
  67. String tag = reader.nextName();
  68. if (tag.equals("address")) {
  69. String address = reader.nextString();
  70. addressText.setText(address);
  71. } else if (tag.equals("work")) {
  72. String work = reader.nextString();
  73. workText.setText(work);
  74. }
  75. ...
  76. else {
  77. reader.skipValue();//忽略
  78. }
  79. }
  80. reader.endObject();
  81. }

上面代码有省略,因为好长…运行图如下:

五招过完,多谢指教!

总结

以上几乎就是 JSO N数组的所有情况了,这五招也几乎能全部搞定!不得不说,GSON 确实比较强大,强大在于可以将 JSON 直接解析成对象,比以前的手动去解析方便太多,当然 fastJson 也能实现这点,但是这东西还是官方的用的顺手。

在学习的过程中,也是一步一步来的,所以文章也是学习的过程,从简单的例子学到关键内容,再解决复杂情况。由于文章写得仓促,如有疑问或错误,欢迎交流与指正,谢谢!

参考资料

灵活组装Json的数据使用Gson的JsonParser和JsonReader解析Json详解例子

使用Gson解析复杂的json数据 – tkwxty

JsonElement的简单说明 – chunqiuwei

Java进阶(四)Java反射TypeToken解决泛型运行时类型擦除的有关问题解决

项目源码

GsonArrayDemo – IamXiaRui – Github

来自:http://www.iamxiarui.com/2016/08/30/android:用gson-五招之内搞定任何json数组/

[转] Android:用GSON 五招之内搞定任何JSON数组的更多相关文章

  1. [转]用GSON 五招之内搞定任何JSON数组

    关于GSON的入门级使用,这里就不提了,如有需要可以看这篇博文 <Google Gson的使用方法,实现Json结构的相互转换> ,写的很好,通俗易懂. 我为什么写这篇文章呢?因为前几晚跟 ...

  2. 工作必备,五分钟如何搞定Excel甘特图

    工作必备,五分钟如何搞定Excel甘特图  https://www.sohu.com/a/212628821_641930 EXCEL中如何给图表添加标题 1.选中图表 >> [布局] 菜 ...

  3. OpenCV3.4.1快速集成到Android studio中,10分钟搞定

    OpenCV3.4.1快速集成到Android studio中,10分钟搞定     转载 https://blog.csdn.net/yu540135101/article/details/8259 ...

  4. Python 五个知识点搞定作用域

    Python 五个知识点搞定作用域 1.块级作用域 想想此时运行下面的程序会有输出吗?执行会成功吗? #块级作用域 if 1 == 1: name = "lzl" print(na ...

  5. Android使用Gson(相当于C#的Newtonsoft.Json)非常好用

    C#转Java有一段时间了,之前做ASP.NET WebAPI微软竟将第三方类库Newtonsoft.Json作为VS新建MVC和WebAPI项目默认必备的Json工具Nuget包,可想而知这个包有多 ...

  6. python一招完美搞定Chromedriver的自动更新

    日常的web自动化过程中,我们常常用python selenium库来操纵Chrome浏览器实现网页的自动化.这其中有个比较头疼的问题:Chrome的更新频率非常频繁,与之对应的Chromedrive ...

  7. 五句话搞定JavaScript作用域

    JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走遍天下都不怕... 一.“JavaScript中无块级作用域” 在Java或C# ...

  8. 【】五句话搞定JavaScript作用域

    JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走遍天下都不怕... 一.“JavaScript中无块级作用域” 在Java或C# ...

  9. 五分钟轻松搞定产品需求文档!这可能史上最全PRD文档模板

    本文由  @JustWu 原创发布于社区 为什么写这篇文章? 第一:写PMCAFF的PRD文档,大家都是用户,比较好参考与理解,方便大家来找我写的不好的地方. 第二:我在自学PRD文档的编写过程中,总 ...

随机推荐

  1. 当集合里存储的是URL时的一些问题总结

    先看道题吧: package com.lk.C; import java.net.MalformedURLException; import java.net.URL; import java.uti ...

  2. Luogu 3957 [NOIP2017]普及组 跳房子

    写了好久,感觉自己好菜,唉…… 首先发现这个$g$的取值具有单调性,可以想到二分答案,然后考虑用$dp$来检验,这样子可以写出朴素的转移方程: 设$f_i$表示以$i$结尾的最大价值,那么有$f_i ...

  3. csv、txt读写及模式介绍

    1读写模式 r以读方式打开文件,可读取文件信息 w已写方式打开文件,可向文件写入信息.如文件存在,则清空,再写入 a以追加模式打开文件,打开文件可指针移至末尾,文件不存在则创建 r+以读写方式打开文件 ...

  4. urllib2设置代理

    #coding=utf-8 #公司网络只有连接vpn跳板机才能使用该模块 import urllib2 proxy_handler=urllib2.ProxyHandler({'http':'http ...

  5. Reading——The Non-Designer's Design Book

    看这本书的时候真的好恨没有CS7在手><,不然我百度几张图来模拟下,体验下设计的快感. 人们总是很容易注意到在他们潜意识里存在的东西,比如说这个图:    我们很容易联想到微信,但是3   ...

  6. python核心编程第3章课后题答案(第二版55页)

    3-4Statements Ues ; 3-5Statements Use\(unless part of a comma-separated sequence in which case \ is ...

  7. linux系统下ipmitool添加BMC帐号密码

    需求:已知BMC帐号id2为root管理员帐号,添加id5bmc帐号 工具:ipmitool version 1.8.14 系统:CentOS release 6.6 (Final) 1,通过yum安 ...

  8. SQL SERVER2008 R2修改Server Collation--文檔沒細看先存下來.

    HOW TO: RESTORE THE MASTER DATABASE IN SQL SERVER 2012 by Thomas LaRock | Jan 14, 2014 | MSSQL, SQL ...

  9. Qt绘制简单的风向玫瑰图代码

    1.绘制简单的风向玫瑰图代码2.主要使用QPainter3.在子widget上绘制需要使用widget监视事件 eventfilter update();//更新界面 //镜头12 QPainter ...

  10. 以太坊系列之五: p2p的nat模块--以太坊源码学习

    p2p的nat模块 该模块相对比较简单,因为nat的真正实现并不在此模块,主要是使用了第三方的nat-upnp和nat-pmp来实现真正的穿透(端口映射). 对外公布的接口 ```go // An i ...