Json和Gson的介绍与简单使用

    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的设定是将它里面的类用作存储功能。
org.json's document

获取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来进行序列化有如下几点缺陷

  1. 得到的Json数据是无序的;
  2. 无论是Map还是JSONObject的put()方法都没办法直接放null值否则会被省略不作序列化;
  3. 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")

Annotation to change SerializedName
    那么在序列化时就会给他自动命名为”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的神器,也有一套类似的方法。但此前先说一些概念性的东西:

  1. Gson中也有一个叫JsonObject的类,跟之前org.json无论是名字(前面的是JSONObject)还是用处都很像(都是用来存储数据),但还是有不一样的地方。
  2. Gson的JsonObject也支持通过Json字符串或者用addpProperty()方法(相当于前面JSONObject的put方法)来构建,因为麻烦第二种就不再演示了,大家可以自己去试一下。
  3. 存在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的优势

  1. 解决了null的歧义问题,当序列化的时候,如果对象的某个字段为null,是不会输出到Json字符串中的。当反序列化的时候,某个字段在Json字符串中找不到对应的值,就会被赋值为null。
  2. 自定义类型自动转换,且利用注解能支持字段过滤,支持任意的复杂对象转换。
  3. 代码量明显大幅度减少。
  4. 定义了一套Json数据类型,能够生成可压缩和可读的Json的字符串输出。

最后的话

    在这里已经大致说完org.json和Gson的基本用法和介绍了,但真的仅仅是基本用法,其实Gson还有更多的像自定义Gson配置,各种注解过滤等等,不过对于简单的Json数据转换已经比较足够去应用了。
    用了Gson之后对比起org.json,肉眼可见的代码量减少,而且在反序列化时对类型也毫无阻碍,不用换来换去。但是单纯是这一点点使用例子与介绍的话实际上很难完全展示出Gson全部魅力,这也仅仅只是冰山一角。有机会的话会试着写更多Gson的用法吧。


参考链接

  1. ABOUT JSON
  2. org.json DOCUMENT
  3. JSONOBJECT NULL SOLUTION
  4. GSON USER GUIDE
  5. GSON DOCUMENT
  6. COMPARISON AMONG JSON LIBRARY