JSON 的那些事
什么是 JSON?
维基百科对它的定义是:JSON(JavaScript Object Notation)是一种由道格拉斯·克罗克福特构想设计、轻量级的数据交换语言,以文字为基础,且易于让人阅读。尽管 JSON 是 Javascript 的一个子集,但 JSON 是独立于语言的文本格式,并且采用了类似于C语言家族的一些习惯。戳我进入维基百科 JSON 词条
后来有个好事之徒把上面的那种处理方式称为 AJAX 即 “Asynchronous Javascript And XML”(异步 JavaScript 和 XML), 其实异步挺好, 但是 XML 就很不爽了。比如说服务器返回了下面这段 xml :
1 2 3 4 5 6 7 | <book> <isbn>978-7-229-03093-3</isbn> <name>三体</name> <author>刘慈欣</author> <introduction>中国最牛的科幻书</introduction> <price>38.00</price> </book> |
真正的数据很少, 标签(像<name>这样的)反而占了大头, 把数据都给淹没了。
XML 的语义性强,也比较直观,与之相比,JSON 的主要优势在于它的体积更小,在网络上传输时就可以更省流量。可以看出 JSON 也是顺应趋势,为 JavaScript 而生,而今已在数据传输上占据一席之地。
而现在,RESTful API 也是一种流行趋势。就现在我所在的工作室,后台使用 Python 和 flask 框架,RESTful API。所以项目上正好碰上这样的 JSON 数据处理,便作下此篇。
Gson 的那些事
1. Gson 的来历
Gson(又称 Google Gson)是 Google 公司发布的一个开放源代码的 Java 库,主要用途为序列化 Java 对象为 JSON 字符串,或反序列化 JSON 字符串成 Java 对象。
2. Gson 在 Android Studio 中的引入
首先是 Gson 的引入,在 Android Studio 界面按下 Ctrl + Shift + Alt + s 快捷键,调出 Project Structure 界面,选择 Modules 下 app 的 Dependencies 选项,点击 + 号,选择添加一个 Library dependency 。
然后搜索所需要的第三方库即可,这里我们需要的是 gson,输入 com.google.code.gson:gson 就会看到有相应的结果显示出来了,我们直接添加导入即可。
当然,你也可以使用 build.gradle 或者下载并引入相应的 jar 包,这里就不再赘余了,而且也没有这方法来的快。
3. Gson 的使用
Gson 的使用非常简单,只需要 new 一个 Gson() 对象即可:
1 2 | Gson gson = new Gson(); Example example = gson.fromJson(exampleJsonString, Example.class); |
详情参见 Google 官方文档
而反序列化 JSON 数据的重点还是在这里的 Example 实体类(或是常说的 JavaBeans )的书写。
JavaBeans 的那些事
1. 什么是 JavaBeans?
维基百科对它的定义是:JavaBeans 是 Java 中一种特殊的类,可以将多个对象封装到一个对象 ( bean ) 中。特点是可序列化,提供无参构造器,提供 getter 方法和 setter 方法访问对象的属性。名称中的 “ Bean ” 是用于 Java 的可重用软件组件的惯用叫法。戳我进入维基百科 JavaBeans 词条
我对 JavaBean 的理解是,Java 为了做到所承诺的向后兼容,于是定义了这么一套规范,JavaBean 里的对象设为私有,将对象的 set 和 get 等方法暴露出来。也就是说这只是一种规范约束。而这种约束也体现了 Java 封装的思想,将对象设为私有变量,使得外部变量不能访问,收紧权限,同时又提供 set 和 get 方法来释放一些权限,使得外部可以间接的对属性值修改从而改变类内部变量的状态,这样的封装,隐藏了具体的实现细节,也降低了耦合性。
解析的那些事
且看这解析三部曲:观、构、干
1. 解析第一步:观
要解析数据,第一步肯定就是观察数据啦!以下均以实际项目中的数据处理作为事例。在写图书馆功能模块时,调用后台所给的 API ,返回借阅信息的 json 数据如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | { "data": [ { "author": "郑斌编著", "barCode": "N1036673", "borrowDate": "2016-05-24", "check": "12F5319", "returnDate": "2016-09-18", "title": "黑客攻防入门与进阶" }, { "author": "周颖等编著", "barCode": "A3421820", "borrowDate": "2016-05-24", "check": "757BBDA", "returnDate": "2016-09-18", "title": "程序员的数学思维修练:趣味解读" }, { "author": "(美) Paul Graham著", "barCode": "N1401694", "borrowDate": "2016-05-24", "check": "63B1A99", "returnDate": "2016-09-18", "title": "黑客与画家:来自计算机时代的高见" }, { "author": "(美) 萨旺特·辛格著", "barCode": "A1449697", "borrowDate": "2016-05-24", "check": "AFFA299", "returnDate": "2016-09-18", "title": "大未来:移动互联时代的十大趋势:implications for our future lives" }, { "author": "陈根著", "barCode": "AN1456291", "borrowDate": "2016-05-24", "check": "7A3B7CE", "returnDate": "2016-09-18", "title": "可穿戴设备:移动互联网新浪潮" }, { "author": "李维勇主编", "barCode": "AN145627", "borrowDate": "2016-05-24", "check": "E8909E9", "returnDate": "2016-09-18", "title": "Android UI设计" } ], "message": "done", "status": 1 } |
在这里的 json 最外面是一个 {} ,里面有 { "data": "something", "message": "someting", "status": "something"},接着 data 里面是一个 [] ,里面包含着 [ {"author": "something", "barCode": "something", "borrowDate": "something", "check": "something", "returnDate": "something", "title": "something"}, ***],而每一个 {} 里的键名和顺序都是一样的。
2. 解析第二步:构
分析完数据,接下来就是要去构造实体类啦!
其中 {} 所对应的是一个类,[] 所对应的是 List 或者是数组,可以看到最外层是由 data 、message、status 组成,而 data 里面是一个 List 。由此看来,我们可以先写出最外层的实体类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public class Root { private List<Data> data; private String message; private int status; public Root() {} public void setMessage(String message) { this.message = message; } public String getMessage() { return this.message; } public void setStatus(int status) { this.status = status; } public int getStatus() { return this.status; } public void setData(List<Data> data) { this.data = data; } public List<Data> getData() { return this.data; } } |
这就是一个标准的 JavaBean 了,提供默认的无参构造器,私有变量,以及暴露出各对象的 set 和 get 方法。
再来看 data 里面的数据,可以看出是每本书的信息所对应的键名是相同的,所以可以用同一个实体类表示,我们接着写这个 Data 类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | public class Data { private String author, barCode, borrowDate, check, returnDate, title;
public void setAuthor(String author) { this.author = author; } public String getAuthor() { return this.author; } public void setBarCode(String barCode) { this.barCode = barCode; } public String getBarCode() { return this.barCode; } public void setBorrowDate(String borrowDate) { this.borrowDate = borrowDate; } public String getBorrowDate() { return this.borrowDate; } public void setCheck(String check) { this.check = check; } public String getCheck() { return this.check; } public void setReturnDate(String returnDate) { this.returnDate = returnDate; } public String getReturnDate() { return this.returnDate; } public void setTitle(String title) { this.title = title; } public String getTitle() { return this.title; } } } |
如果你要把这个 Data 类内嵌到刚才的 Root 类里面,必须声明是静态类型,不然是不能解析,示例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Root { private List<Data> data; private String message; private int status; public Root() {} //这里省略 set 和 get 方法 public static class Data { private String author, barCode, borrowDate, check, returnDate, title; //这里省略 set 和 get 方法 } } |
还有一点就是,在返回的 JSON 数据中,如果有不需要的数据,在构造实体类时,是可以不需要写出来的。
3. 解析第三步:干
实体类都写好了,还有什么好说的,就是干。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public void getLibraryInfo() { //此方法为部分代码,且这里省略网络请求部分
Response response = client.newCall(request).execute(); String responseBody = response.body().string();
Gson gson = new Gson(); Root root = gson.fromJson(responseBody, Root.class); //这里是示例,取出一些数据 status = root.getStatus(); message = root.getMessage();
List<Data> data = root.getData(); title = data.get(1).getTitle(); author = data.get(1).getAuthor(); barCode = data.get(1).getBarCode(); borrowDate = data.get(1).getBorrowDate(); numberOfBooks = data.size(); } |
这里有个点,是在我不懂之前一直疑惑且写了很多无用赘余的代码,就是在 Root root = gson.fromJson(responseBody, Root.class); 这步执行完后,相应的数据其实就已经填充到对应的实体类的变量中,所以直接 get 就可以取到数据啦!那可能有人要问了,这里的 set 方法有什么用,根本就没用上啊。只能说只是你没有碰上那个需求,所以没用上,如果说你的程序里需要在请求回来的数据里设定一些特定的值,此时你就需要用到 set 方法。
这里取出各个数据( List 可以遍历取出),之后你需要什么要的格式和组合就凭君调遣啦!我的习惯是将这个图书馆数据查询获取模块写成一个 Util ,应用中需要用到什么样的数据,在 Util 里写一个对应的方法返回即可。