Leo's Blog

Gson 正确打开姿势(一)

JSON (官网) 是一种文本形式的数据交换格式,它比XML更轻量、比二进制容易阅读和编写,调式也更加方便。其重要性不言而喻。解析和生成的方式很多,Java中最常用的类库有:JSON-Java、Gson、Jackson、FastJson等。
项目中使用了Gson,然而现在发现Gson差点被自己玩坏,所以决定好好学习一下~

Gson 基本用法

Gson 提供了两个方法 fromJson()toJson()方法直接用于解析和生成Json。前者实现反序列化,后者实现序列化。

基本数据类型解析

1
2
3
4
5
Gson gson = new Gson();
int i = gson.fromJson("100", int.class); //100
double d = gson.fromJson("\"99.99\"", double.class); //99.99
boolean b = gson.fromJson("true", boolean.class); // true
String str = gson.fromJson("String", String.class); // String

基本数据类型生成

1
2
3
4
Gson gson = new Gson();
String jsonNumber = gson.toJson(100); // 100
String jsonBoolean = gson.toJson(false); // false
String jsonString = gson.toJson("String"); //"String"

POJO类的生成与解析

1
2
3
4
5
public class User{
public String name;
public int age;
public String emailAddress;
}

生成Json:

1
2
3
Gson gson = new Gson();
User user = new User("怪盗kidou",24);
String jsonObject = gson.toJson(user); // {"name":"怪盗kidou","age":24}

解析Json:

1
2
3
Gson gson = new Gson();
String jsonString = "{\"name\":\"怪盗kidou\",\"age\":24}";
User user = gson.fromJson(jsonString, User.class);

属性重命名

之前我们认为Gson对于服务器json数据的解析,属性名称需要与json中的key保持一致,一一对应。但是我们经常会遇到一些不和谐的情况。。。

@SerializedName注解使用

我们期望的json数据

1
{"name":"leo","age":28,"emailAddress":"leo@example.com"}

服务端返回的json数据

1
{"name":"leo","age":28,"email_address":leo@example.com}

谷歌的大神,怎么可能不考虑这个问题,我们来看一下如何处理,我们只需要将POJO中属性变成

1
2
@SerializedName("email_address")
public String emailAddress;

哦了,重命名的问题搞定~如果接口重用,后端的开发人员也更换了,来了一个偷懒的人。。。出现了下面的数据

1
{"name":"leo","age":28,"emailAddress":"leo@example.com"}

1
{"name":"leo","age":28,"email_address":leo@example.com}
1
{"name":"leo","age":28,"email":leo@example.com}

我们如何处理?

为POJO字段提供属性备选名

SerializedName注解提供了两个属性,上面用到了其中一个,另外还有一个属性alternate(2.4版本以上),接收一个String数组。

1
2
@SerializedName(value = "emailAddress", alternate = {"email", "email_address"})
public String emailAddress;

当上面的三个属性(email_address、email、emailAddress)出现任意一个时都可以得到正确的结果。

Gson泛型使用

Gson 为我们提供了 TypeToken 来实现对泛型的支持。

基本类型数据解析

1
2
3
Gson gson = new Gson();
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
List<String> stringList = gson.fromJson(jsonArray,new TypeToken<List<String>>(){}.getType());

POJO的泛型数据解析

泛型的引入可以减少很多无关的代码,我们现在后台的数据接口数据主要有下面两类:

1
2
3
4
{
message : {"code":0,"message":"操作成功"},
"data":{}
}

1
2
3
4
{
message : {"code":0,"message":"操作成功"},
"data":{}
}

我们真正需要的是 data 所包含的数据,message 中的数据我们只需要统一处理一次。如果我们不定义泛型那么我们就需要这样定义POJO。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class UserBean{
private MessageBean message;
private User data;
public MessageBean getMessage() {
return message;
}
public static class MessageBean {
/**
* code : 0
* message : 操作成功
*/
private int code;
private String message;
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
}

定义成泛型,就会简洁很多

1
2
3
4
public class Result<T>{
private MessageBean message;
private T data;
}

然后我们只需编写data字段所对应的POJO即可,专注于我们的业务逻辑。

解析数据的写法:

1
2
3
Type userType = new TypeToken<Result<User>>(){}.getType();
Result<User> userResult = gson.fromJson(json,userType);
User user = userResult.data;


1
2
3
Type userListType = new TypeToken<Result<List<User>>>(){}.getType();
Result<List<User>> userListResult = gson.fromJson(json,userListType);
List<User> users = userListResult.data;

Gson 流操作

Gson 的流操作是通过stream包下的JsonReader类和JsonWriter来实现的。

Gson 流反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
String json = "{\"name\":\"leo\",\"age\":\"28\"}";
User user = new User();
JsonReader reader = new JsonReader(new StringReader(json));
reader.beginObject(); // throws IOException
while (reader.hasNext()) {
String s = reader.nextName();
switch (s) {
case "name":
user.name = reader.nextString();
break;
case "age":
user.age = reader.nextInt();
break;
case "email":
user.email = reader.nextString();
break;
}
}
reader.endObject(); // throws IOException

fromJson()方法实际上就是通过JsonReader来进行数据解析的,不信? 去看源码喽~

1
2
3
4
5
6
7
8
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
if (json == null) {
return null;
}
StringReader reader = new StringReader(json);
T target = (T) fromJson(reader, typeOfT);
return target;
}

Gson 流序列化

序列化和反序列化始终是成对出现的,咱们这也不能让它落单不是。。。

1
2
3
4
5
6
7
JsonWriter writer = new JsonWriter(new OutputStreamWriter(System.out));
writer.beginObject() // throws IOException
.name("name").value("leo")
.name("age").value(28)
.name("email").nullValue() //演示null
.endObject(); // throws IOException
writer.flush(); // throws IOException

类似的,toJson() 方法自然是通过JsonWriter实现序列化

1
2
3
4
5
public String toJson(Object src, Type typeOfSrc) {
StringWriter writer = new StringWriter();
toJson(src, typeOfSrc, writer);
return writer.toString();
}