JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成,广泛应用于各种数据的交互中,尤其是服务器与客户端的交互。
Gson(又称Google Json)是Google公司发布的一个开放源代码的Java库,主要用途为序列化Java对象为JSON字符串,或反序列化JSON字符串成Java对象。
前言
对于Json的使用大部分人都会知道的一个轻量级库:org.json,现在仍旧有不少人用,不过他对于json格式到bean格式的转换设计的有缺陷而且麻烦(例如复杂类型转换),对于简单的Json序列化操作时如此轻量级的类库是个不错的选择。(毕竟轻量级且五脏俱全)
如今对于Json的类库比较广为人知的有json-lib、Jackson、阿里巴巴的 FastJson、谷歌的 Gson,json-lib依赖的包太多速度也慢就不提了,虽然FastJson在parseJson有算法上的先进性以至能大大提高其速度运作,而Jackson在速度上也相差不多,但Gson在对于复杂的类型转换上有更强的性能,对于标准化的类库来说,Gson更能被称为Json的神器,那么在本篇我会写关于org.json的简单使用与Gson的常见使用来比较两者的用途与用法。
本章代码已上传到github,可以查看代码自己研究。
添加类库依赖
Maven
org.json:
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
Gson:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
Gradle
org.json:
compile group: 'org.json', name: 'json', version: '20180813'
Gson:
dependencies {
implementation 'com.google.code.gson:gson:2.8.5'
}
Json数据与Bean
在正式介绍前简单看看本章会用到的Json数据与相应的JavaBean:
Json
Json数据例子中简单地囊括Json中所有的类型,包括字符串型(string),数字型(number),布尔型(true/false),空型(null)和object型。
{
"name": "Bruce Eckel",
"age": 61,
"male": true,
"representative_books": [
"Thinking in C++",
"Thinking in Java"
],
"job": {
"company": "MindView",
"position": "CEO"
},
"comment": null
}
JavaBean
其中设置了books和representive_works有不同的key,来考察类库的兼容性。而且comment在这不当做属性,只当做返回json数据中的备注。
public class Person {
String name;
int age;
boolean male;
String[] books;
Job job;
//构造方法,getter,setter省略
}
class Job {
String company;
String position;
//构造方法,getter,setter省略
}
org.json
先简单说说org.json的用法吧,他提供的类库十分简单,只有四个常用类一个异常类,其中最为常用的一个类就JSONObject,基本可以用这个类解决比较多的事,org.json的设定是将它里面的类用作存储功能。
获取Json格式数据
其实利用org.json基本上就像是Map<K,V>一样取值存值而已。
JSONObject有几个常用的构造方法,那么我先使用JSONObject来进行序列化:
//字符串转Json
String jsonStr = "{\"name\": \"Bruce Eckel\",\"age\": 61,\"male\": true,\"representative_books\": [\"Thinking in C++\",\"Thinking in Java\"],\"job\": {\"company\": \"MindView\",\"position\": \"CEO\"},\"comment\": null}";
JSONObject strJson =new JSONObject(jsonStr); //传入Json格式字符串
System.out.println("构造参数为String类:"+strJson);
//Map转Json
Map<String,Object> jsonMap = new HashMap<String,Object>();
jsonMap.put("name", "Bruce Eckel");
jsonMap.put("age", 61);
jsonMap.put("male", true);
jsonMap.put("job", new JSONObject("{\"company\": \"MindView\", \"position\": \"CEO\"}"));
jsonMap.put("representative_books", new String[]{ "Thinking in C++","Thinking in Java"});
jsonMap.put("comment",null);
JSONObject mapJson = new JSONObject(jsonMap); //传入Map类型
System.out.println("构造参数为Map类:" + mapJson);
//JavaBean转Json
Person bruce = new Person("Bruce Eckel", 61, true, new String[]{ "Thinking in C++","Thinking in Java"} , new Job("MindView", "CEO"));
JSONObject beanJson = new JSONObject(bruce); //传入Bean类型
System.out.println("构造参数为Bean类:" + beanJson);
//用无参构造方法传参数的方式
JSONObject jsonObject = new JSONObject(); //无参构造器
jsonObject.put("name","Bruce Eckel");
jsonObject.put("age", 61);
jsonObject.put("male", true);
jsonObject.put("job", new JSONObject("{\"company\": \"MindView\", \"position\": \"CEO\"}")); //此处用的是JSONObject
jsonObject.put("representative_books", new String[]{ "Thinking in C++","Thinking in Java"});
jsonObject.put("comment", "null"); //此处输入的是"null"
System.out.println("构造参数为空:"+jsonObject);
输出效果如下:
构造参数为String类:{“name”:”Bruce Eckel”,”comment”:null,”representive_works”:[“Thinking in C++”,”Thinking in Java”],”job”:{“company”:”MindView”,”position”:”CEO”},”age”:61,”male”:true}
构造参数为Map类:{“name”:”Bruce Eckel”,”representive_work”:[“Thinking in C++”,”Thinking in Java”],”job”:{“company”:”MindView”,”position”:”CEO”},”age”:61,”male”:true}
构造参数为Bean类:{“books”:[“Thinking in C++”,”Thinking in Java”],”gender”:true,”name”:”Bruce Eckel”,”job”:{},”age”:61}
构造参数为空:{“representive_work”:[“Thinking in C++”,”Thinking in Java”],”name”:”Bruce Eckel”,”comment”:”null”,”job”:{“company”:”MindView”,”position”:”CEO”},”age”:61,”male”:true}
可见JSONObject来进行序列化有如下几点缺陷:
- 得到的Json数据是无序的;
- 无论是Map还是JSONObject的put()方法都没办法直接放null值否则会被省略不作序列化;
- JavaBean中的自定义类型属性无法被序列化。
对于第一点的解决方法:
org.json提供了一个有序的类JSONStringer,用法如下
JSONStringer jsonStringer = new JSONStringer();
jsonStringer.object();
jsonStringer.key("name");
jsonStringer.value("Bruce Eckel");
jsonStringer.key("age");
jsonStringer.value(61);
jsonStringer.key("male");
jsonStringer.value(true);;
jsonStringer.key("job");
jsonStringer.object();
jsonStringer.key("company");
jsonStringer.value("MindView");
jsonStringer.key("position");
jsonStringer.value("CEO");
jsonStringer.endObject();
jsonStringer.key("representative_works");
jsonStringer.array();
jsonStringer.value("Thinking in C++");
jsonStringer.value("Thinking in Java");
jsonStringer.endArray();
jsonStringer.key("comment");
jsonStringer.value("null");
jsonStringer.endObject();
System.out.println(jsonStringer.toString());
输出效果如下
{“name”:”Bruce Eckel”,”age”:61,”male”:true,”job”:{“company”:”MindView”,”position”:”CEO”},”representative_works”:[“Thinking in C++”,”Thinking in Java”],”comment”:”null”}
虽然是麻烦了点(非常麻烦)…毕竟他是有序的。而且一般情况用JSONObject也是够的因为有序不一定有用,而且JSONStringer虽然较为麻烦,但是他对于多重嵌套的问题有很大帮助。但要注意object()和endObject()、array()和endArray(),一定要匹配,否则就会抛出JSONException异常。
对于第二点的解决方法:
JSONObject对于null的识别是有坑的,想要赋予值为空的属性时得以”null”的方式传入(可以参考上述无参的构造方式中传”null”的方法-第29行。)
那么问题来了,如果想传值为”null”的字符串怎么办?
那可能只能通过直接处理字符串的方式了:
jsonObject.toString.replaceAll(":null,", ":\"null\",")
对于第三点的解决方法:
org.json库无法识别自定义属性,所以只好用嵌套的方式,即JSONObject里包含JSONObject的方式来代替。(可以参考上述无参构造方式中的job-第27行)
获取Json的属性值
这里用回之前的JSONObject。org.json压根就没做反序列化,如果手动来做的话这样是比较快的方法了,如果不是为了演示用法真不想做一次,对比起任何一个类库即使是json-lib都比他方便多了。当然,如果你只是为了取得里面的部分数据绝对是可以使用org.json的。
String jsonStr = "{\"name\": \"Bruce Eckel\",\"age\": 61,\"male\": true,\"representive_works\": [\"Thinking in C++\",\"Thinking in Java\"],\"job\": {\"company\": \"MindView\",\"position\": \"CEO\"},\"comment\": null}";
JSONObject strJson =new JSONObject(jsonStr); // 传入字符串
JSONArray jsonArray = strJson.getJSONArray("representive_works");
List<String> lists = new ArrayList<String>();
for (int i = 0; i < jsonArray.length(); i++) {
lists.add(i,jsonArray.optString(i));
}
String[] books = new String[2];
lists.toArray(books);
Job job = new Job(strJson.getJSONObject("job").getString("company"), strJson.getJSONObject("job").getString("position"));
Person bruce = new Person(strJson.getString("name"),strJson.getInt("age"),strJson.getBoolean("male"), books, job);
System.out.println(bruce.name+" "+bruce.age+" "+bruce.male+" "+bruce.books[0]+" "+bruce.books[1]+" "+bruce.job.company+" "+bruce.job.position);
输出效果如下
Bruce Eckel 61 true Thinking in C++ Thinking in Java MindView CEO
以上就是基本的org.json类库的使用方法了,当然还有像JsonTokener和JSONWritter之类的类,都是没什么必要的,基本用JSONObject、JSONStringer、JSONArray这几个类可以完成业务。这也是org.json轻便之处,如果只是少量的Json操作,那用org.json这种轻量级类库再适合不过了。
Gson
Gson中最重要的类可能就是Gson类了,但Gson中是不像其他类那样以JSONObject这样像Map一样的类存储数据的,Gson相当于只为了使用其成员方法。
在操作之前,记得前面设计数据key和JavaBean属性时名字有不同的吗?序列化时如果不作任何操作只会用回原来属性名,而Gson给出了用注解的方式来处理的方法:
@SerializedName(value = "representative_books")
那么在序列化时就会给他自动命名为”representative_books”,反序列化的时候就自动识别key为”representative_books”的语句
序列化
序列化就是将JavaBean到Json字符串的过程
Person bruce = new Person("Bruce Eckel", 61, true, new String[]{ "Thinking in C++","Thinking in Java"} , new Job("MindView", "CEO"));
Gson gson = new Gson();
String json = gson.toJson(bruce);
System.out.println(json);
输出效果
{“name”:”Bruce Eckel”,”age”:61,”male”:true,”representative_books”:[“Thinking in C++”,”Thinking in Java”],”job”:{“company”:”MindView”,”position”:”CEO”}}
反序列化
反序列化就是将字符串转换成JavaBean的过程
Gson gson = new Gson();
String json = "{\"name\":\"Bruce Eckel\",\"age\":61,\"male\":true,\"representative_books\":[\"Thinking in C++\",\"Thinking in Java\"],\"job\":{\"company\":\"MindView\",\"position\":\"CEO\"},\"comment\":null}";
Person bruce = gson.fromJson(json, Person.class);
System.out.println(bruce.getName()+" "+bruce.getAge()+" "+bruce.isMale()+" "+bruce.getBooks()[0]+" "+bruce.getBooks()[1]+" "+bruce.getJob().company+" "+bruce.getJob().position);
输出效果
Bruce Eckel 61 true Thinking in C++ Thinking in Java MindView CEO
获取Json数据
看到这里可能就会说,org.json的前面两点实际上跟Gson的序列化反序列化不太一样吧,org.json是获取数据,Gson是直接序列化了,那如果我只是要获取数据Gson还得创一个JavaBean不更加麻烦吗?实际上是不一定要JavaBean的,相信谷歌设计者也不会这么蠢。
Gson作为Json的神器,也有一套类似的方法。但此前先说一些概念性的东西:
- Gson中也有一个叫JsonObject的类,跟之前org.json无论是名字(前面的是JSONObject)还是用处都很像(都是用来存储数据),但还是有不一样的地方。
- Gson的JsonObject也支持通过Json字符串或者用addpProperty()方法(相当于前面JSONObject的put方法)来构建,因为麻烦第二种就不再演示了,大家可以自己去试一下。
- 存在JsonObject里的数据称为JsonElement,而JsonElement有四种类型(意思就是说这四个类都是继承于JsonElement的):JsonPrimitive(相当于number或者string或者boolean)、JsonArray(任意类型的JsonElement的集合,也可以混合类型)、JsonNull(相当于null)、JsonObject(嵌套自己类型),更多的详情请麻烦看下面的例子吧:
String json = "{\"name\":\"Bruce Eckel\",\"age\":61,\"male\":true,\"representative_books\":[\"Thinking in C++\",\"Thinking in Java\"],\"job\":{\"company\":\"MindView\",\"position\":\"CEO\"},\"comment\":null}";
JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject();
System.out.println(jsonObject.getAsJsonPrimitive("name"));
System.out.println(jsonObject.getAsJsonArray("representative_books"));
System.out.println(jsonObject.getAsJsonObject("job"));
输出效果如下
“Bruce Eckel”
[“Thinking in C++”,”Thinking in Java”]
{“company”:”MindView”,”position”:”CEO”}
而通过debug以上代码,效果如下图
从这里就能很清晰的看到Gson提供的JsonObject的用法和其类型的解释了。可见Gson对于Json数据获取也是比较方便的。
可见Gson的优势
- 解决了null的歧义问题,当序列化的时候,如果对象的某个字段为null,是不会输出到Json字符串中的。当反序列化的时候,某个字段在Json字符串中找不到对应的值,就会被赋值为null。
- 自定义类型自动转换,且利用注解能支持字段过滤,支持任意的复杂对象转换。
- 代码量明显大幅度减少。
- 定义了一套Json数据类型,能够生成可压缩和可读的Json的字符串输出。
最后的话
在这里已经大致说完org.json和Gson的基本用法和介绍了,但真的仅仅是基本用法,其实Gson还有更多的像自定义Gson配置,各种注解过滤等等,不过对于简单的Json数据转换已经比较足够去应用了。
用了Gson之后对比起org.json,肉眼可见的代码量减少,而且在反序列化时对类型也毫无阻碍,不用换来换去。但是单纯是这一点点使用例子与介绍的话实际上很难完全展示出Gson全部魅力,这也仅仅只是冰山一角。有机会的话会试着写更多Gson的用法吧。