Gson的使用方法

前言

最近写的插件有些地方需要将我的对象保存在磁盘中,虽然确实也可以使用序列化来完成这一操作,但我还是没有这么做。
我需要将它保存至MySQL里,当然MySQL也支持直接操作二进制数据,不过我总是觉得这样不够优雅。
Srar大佬说可以转成Json,然后再保存。
于是我就这么做了。

Json简介

JSON (JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
简单来说,就是让你的数据以一种更方便阅读的方式来保存,同时也能很容易的再次被读取进你的程序。
请看下面的代码:(省略了getter和setter)

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
class Player {
//玩家的编号
private int ID;
//玩家是否在线
private boolean online;
//玩家的年龄
private int age;
//玩家的名字
private String name;
//玩家的简介
private List<String> introduction;
//玩家与其他玩家的亲密值
private Map<Integer, Integer> intimacy;

public Player(int ID, boolean online, int age, String name, List<String> introduction, Map<Integer, Integer> intimacy) {
this.ID = ID;
this.online = online;
this.age = age;
this.name = name;
this.introduction = introduction;
this.intimacy = intimacy;
}

@Override
public String toString() {
return "Player{" +
"ID=" + ID +
", online=" + online +
", age=" + age +
", name='" + name + '\'' +
", introduction=" + introduction +
", intimacy=" + intimacy +
'}';
}
}

对于一个Player对象:

1
2
3
4
5
6
7
8
List<String> list = new ArrayList<>();
list.add("你好,世界!");
list.add("我是叁只仓鼠!");
Map<Integer,Integer> map = new HashMap<>();
map.put(1,100);
map.put(2,5000);
map.put(3,-1000);
Player player = new Player(0,true,18,"叁只仓鼠",list,map);

如果你用序列化保存……那你对这个存档的编辑工作将会很难进行。
如果你存成一个.txt

1
2
3
4
ID: 0
name: 叁只仓鼠
online: true
......

这样确实很容易编辑,但是写起代码来会有点难受,所以为什么不用一种简单的方法来做这些呢?

如果使用Gson把它转成Json格式:{"ID":0,"online":true,"age":18,"name":"叁只仓鼠","introduction":["你好,世界!","我是叁只仓鼠!"],"intimacy":{"1":100,"2":5000,"3":-100,"4":-900}}
再用工具 格式化一下,就变得非常直观易读了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"ID": 0,
"online": true,
"age": 18,
"name": "叁只仓鼠",
"introduction": [
"你好,世界!",
"我是叁只仓鼠!"
],
"intimacy": {
"1": 100,
"2": 5000,
"3": -100,
"4": -900
}
}

而且格式化之后并不影响程序再次读入这条Json,这样的操作十分便捷,也让我们的数据十分易于操作。

Gson简介

Java本身并不附带Json解析器,所以我们得使用第三方工具来操作,这里推荐Gson。
Gson是google写的一个开源Java操作Json工具。
Maven:

1
2
3
4
5
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>

Gson构造器

先获取一个Gson构造器,这东西能把你的的对象给直接转成Json,也能把Json转成一个对象。
如果你想使用默认设置,直接new Gson();或者new GsonBuilder().create();
如果你想修改设置,使用new GsonBuilder().create();
比如说,你的对象里有存了一个Map<Player,Integer>,这时这个Map的Key是一个复杂对象形式,你想把它转成Json,那么你得先设置一下你的转换器。

1
2
3
4
5
6
Gson gson = new GsonBuilder()
.setLenient()// json宽松
.enableComplexMapKeySerialization()//支持Map的key为复杂对象的形式
.serializeNulls() //智能null
.setPrettyPrinting()// 调教格式
.create();

更多GsonBuilder设置请看Doc
当然默认情况下我们还是直接new GsonBuilder().create();

把你的对象转成Gson

很简单,两行代码

1
2
Gson gson = new GsonBuilder().create();
System.out.println(gson.toJson(player));

输出{"ID":0,"online":true,"age":18,"name":"叁只仓鼠","introduction":["你好,世界!","我是叁只仓鼠!"],"intimacy":{"1":100,"2":5000,"3":-100,"4":-900}}
这里要注意一下,如果你的对象里存有map,而你这个map的key是复杂对象的话。
请设置new GsonBuilder().enableComplexMapKeySerialization().create();

把Json转成你的对象

在执行这一步之前你要十分肯定这条Json的每一个元素都与你的对象类型一一确定。
使用gson.fromJson();
这个方法接收两个参数,一个String,一个Class。(或者一个Reader,一个Class
如果你从数据流中读取的Json,那你可以直接套个InputStreamReader传给它。后面一个Class是帮助识别的。告诉Gson你这个json对象对应了你Java代码里的哪个类。

1
2
3
4
5
6
7
8
9
10
11
   public static void main(String[] args) {
Gson gson = new GsonBuilder().create();
File file = new File("E:\\test\\player.json");
try {
InputStreamReader reader = new InputStreamReader(new FileInputStream(file));
Player p = gson.fromJson(reader,Player.class);
System.out.println(p.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

输出:Player{ID=0, online=true, age=18, name='叁只仓鼠', introduction=[你好,世界!, 我是叁只仓鼠!], intimacy={1=100, 2=5000, 3=-100, 4=-900}}

部分读取

有时候我们并不是想要直接把一个json转成Java对象,而是读取这条json的部分内容,这时我们可以先把他读成一条JsonElement(json条目)。

JsonParser

JsonParser是一个解析器,用于把一条文本解析成一个json对象。
使用也很简单,先new一个出来:JsonParser parser = new JsonParser();
然后parser.parse(Reader reader);或者parser.parse(String string);
具体reader和string是啥你们都懂的。

JsonElement

在读入的时候,解析器并不知道它自己读入的到底是一个对象,还是一个数组,又或者是其他什么的,所以我们在读入的时候只把他当成一条json条目。

1
2
3
4
5
6
7
8
9
10
11
12
13
   public static void main(String[] args) {
File file = new File("E:\\test\\player.json");
try {
InputStreamReader reader = new InputStreamReader(new FileInputStream(file));
//解析器
JsonParser parser = new JsonParser();
//解析json
JsonElement element = parser.parse(reader);

} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

剩下的操作就要靠我们来解决,我们在写这段代码的时候一定要十分清楚,我们的程序到底入读了什么,数组?对象?null?
反正我是很清楚啦,我读的是一个object。

JsonObject

既然很清楚我读的这一条是object,那就先转换一下吧:JsonObject object = element.getAsJsonObject();
然后我就可以读取这条json的数据了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
File file = new File("E:\\test\\player.json");
try {
InputStreamReader reader = new InputStreamReader(new FileInputStream(file));
//解析器
JsonParser parser = new JsonParser();
//解析json
JsonElement element = parser.parse(reader);
//转成JsonObject
JsonObject object = element.getAsJsonObject();
System.out.println("ID: " + object.get("ID"));
System.out.println("在线: " + object.get("online"));
System.out.println("年龄: " + object.get("age"));
System.out.println("名称: " + object.get("name"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

输出:

1
2
3
4
ID: 0
在线: true
年龄: 18
名称: "叁只仓鼠"

值得一提的是,object.get(String string);这个方法返回的也是一个JsonElement。

JsonArray

Json里也有数组存储着,所以在读取出来的时候,我们也要相应地转成Array,才能方便地进行下一步操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) {
File file = new File("E:\\test\\player.json");
try {
InputStreamReader reader = new InputStreamReader(new FileInputStream(file));
//解析器
JsonParser parser = new JsonParser();
//解析json
JsonElement element = parser.parse(reader);
//转成JsonObject
JsonObject object = element.getAsJsonObject();
//取得object中名为“introduction”的条目
JsonArray array = object.getAsJsonArray("introduction");
System.out.println("个人简介:");
for(int i=0;i<array.size();i++) {
System.out.println(array.get(i));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

输出:

1
2
3
个人简介:
"你好,世界!"
"我是叁只仓鼠!"

复杂对象

在这里有一个稍微有点难的事,就是这一条属性private Map<Integer, Integer> intimacy;
它是一个map,我们应该如何读取呢?这样的话又需要使用到GsonBuilder了,但是这样的话我们得先告诉他我们的HashMap到底长啥样,Key是啥,Value又是啥。
直接HashMap<Integer, Integer> map = gson.fronJson(object.get("intimacy"),new HashMap<Integer, Integer>().getClass());是行不通的。
这时候我们需要用到反射:
Type type = new TypeToken<HashMap<Integer,Integer> >(){}.getType();
需要用到的包:
import com.google.gson.reflect.TypeToken;
最后代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) {
Gson gson = new GsonBuilder().create();
File file = new File("E:\\test\\player.json");
try {
InputStreamReader reader = new InputStreamReader(new FileInputStream(file));
//解析器
JsonParser parser = new JsonParser();
//解析json
JsonElement element = parser.parse(reader);
//转成JsonObject
JsonObject object = element.getAsJsonObject();
//获得type
Type type = new TypeToken<HashMap<Integer,Integer> >(){}.getType();
//传递JsonElement和type过去
HashMap map = gson.fromJson(object.get("intimacy"),type);
System.out.println(map);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

本章完

如果本篇文章对你有帮助,请为我提供一个吃冰激凌的机会!
(热死了QAQ)

人生不易,仓鼠断气