JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。Gson是Google提供的开源库,可以将Java对象转换为JSON数据,也可以JSON数据转换为Java对象。本文简要介绍Gson库基本使用方法。
解析JSON
本例中使用的JSON数据是实际项目中通过luci获取到的,这里只是作为数据示例。
先以一段简单的Http Response的JSON数据开始:1
2
3
4
5{
"id": 1,
"result": "a5823b98e0dc91cecddb5516e7e85c85",
"error": null
}
那么它对应的Java对象可以表示为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public class Response {
private int id;
private String result;
private String error;
public int getId() {
return id;
}
public String getResult() {
return result;
}
public String getError() {
return error;
}
private Response() {
}
}
那么可以使用Gson将JSON数据解析为Response
对象:1
2
3
4Response parse(String json) {
Gson gson = new Gson();
return gson.fromJson(json, Response.class);
}
对于有些代码规范需要将在变量名前面加上m
前缀,那么变量名就和JSON字段名不一样了,这个时候就需要用到注解@SerializedName
。将Response
类修改为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public class Response {
@SerializedName("id")
private int mId;
@SerializedName("result")
private String mResult;
@SerializedName("error")
private String mError;
public int getId() {
return mId;
}
public String getResult() {
return mResult;
}
public String getError() {
return mError;
}
private Response() {
}
}
如果Response
的JSON数据发生了变化:1
2
3
4
5
6
7
8{
"id": 2,
"result": null,
"error": {
"message": "Method not found.",
"code": -32601
}
}
可以看到error
字段不为空了。现在需要修改mError
的类型:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public class Response {
...
@SerializedName("error")
private Error mError;
public Error getError() {
return mError;
}
public static class Error{
@SerializedName("message")
private String mMessage;
@SerializedName("code")
private int mCode;
public String getMessage() {
return mMessage;
}
public int getCode() {
return mCode;
}
}
...
}
这样不用修改parse
方法也可以解析JSON数据为Response
对象了。如果JSON数据result
字段发生了变化: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{
"id": 2,
"result": [
{
"band": "5G",
"type": "mt7612e",
"vendor": "ralink",
"autoch": "2",
".index": 0,
"channel": "0",
".name": "mt7612e",
".type": "wifi-device",
".anonymous": false
},
{
"band": "2.4G",
"type": "mt7628",
"vendor": "ralink",
".index": 2,
"channel": "0",
".name": "mt7628",
"auotch": "2",
".type": "wifi-device",
".anonymous": false
}
],
"error": null
}
可以看到JSON数据的result
是一个JSON数组,而前面的result
是字符串,如果要兼容前面的JSON解析,首先要修改Response
中的mResult
:1
2
3
4
5
6
7
8
9public class Response {
...
@SerializedName("result")
private JsonElement mResult;
...
public JsonElement getResult() {
return mResult;
}
}
其次,在Gson初步解析后需要判断mResult
的具体类型来进一步解析:1
2
3
4
5
6
7
8
9
10
11void parse(String json, ParseCallback callback) {
Gson gson = new Gson();
Response response = gson.fromJson(json, Response.class);
JsonElement resultElement = response.getResult();
if (resultElement.isJsonPrimitive()) {
// parse string
} else if (resultElement.isJsonArray()) {
// parse array
}
}
由于解析的结果不止一种类型,所以这里用一个回调来返回不同类型的解析结果:1
2
3
4
5interface ParseCallback {
void onResult(String result);
void onResult(List<WifiDevice> devices);
}
其中WifiDevice
对应于JSON数组中的具体类型,这里就不给出具体定义了。接下来看看进一步的解析:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void parse(String json, ParseCallback callback) {
...
if (resultElement.isJsonPrimitive()) {
String result = resultElement.getAsString();
if (callback != null) {
callback.onResult(result);
}
} else if (resultElement.isJsonArray()) {
List<WifiDevice> devices = new ArrayList<>();
JsonArray array = resultElement.getAsJsonArray();
for (JsonElement element : array) {
WifiDevice device = gson.fromJson(element, WifiDevice.class);
devices.add(device);
}
if (callback != null) {
callback.onResult(devices);
}
}
}
这里仅仅举例解析result
字段是两种类型的情况,还有当result
为不同元素类型的数组,这个时候可以定义不同的Response
类(假设事先知道result
字段的具体类型)。
生成JSON数据
相对于JSON解析,把类序列化为JSON数据就简单多了。如果有一个成绩单的类需要生成JSON数据,先来看一下Transcript
类: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
34public class Transcript {
@SerializedName("name")
private String mName;
@SerializedName("grade")
private int mGrade;
@SerializedName("class")
private int mClass;
@SerializedName("subject_scores")
private EnumMap<Subject, Integer> mSubjectScores = new EnumMap<Subject, Integer>(Subject.class);
private Transcript() {
}
public Transcript(String name, int grade, int clazz) {
mName = name;
mGrade = grade;
mClass = clazz;
}
public void addSubjectScore(Subject subject, int score) {
mSubjectScores.put(subject, score);
}
enum Subject {
CHINESE,
ENGLISH,
MATH,
PHYSICAL
}
}
接着通过Gson生成JSON数据:1
2
3
4
5
6
7Gson gson = new Gson();
Transcript transcript = new Transcript("Huntto", 3, 17);
transcript.addSubjectScore(Transcript.Subject.CHINESE, 80);
transcript.addSubjectScore(Transcript.Subject.ENGLISH, 85);
transcript.addSubjectScore(Transcript.Subject.MATH, 98);
transcript.addSubjectScore(Transcript.Subject.PHYSICAL, 83);
String json = gson.toJson(transcript, Transcript.class);
生成的JSON数据为:1
2
3
4
5
6
7
8
9
10
11{
"class": 17,
"grade": 3,
"name": "Huntto",
"subject_scores": {
"CHINESE": 80,
"ENGLISH": 85,
"MATH": 98,
"PHYSICAL": 83
}
}
虽然成功了,但是不知道能不能再次通过Gson解析:
1 | Gson gson = new Gson(); |
解析不成功,报错了:1
2Caused by: java.lang.IllegalArgumentException: field me.huntto.gsondemo.Transcript.mSubjectScores has type java.util.EnumMap, got java.util.LinkedHashMap
at java.lang.reflect.Field.set(Native Method)
意思是不能用EnumMap
,需要使用LinkedHashMap
:1
2
3
4
5
6
7
8
9
10
11
12
13public class Transcript {
...
@SerializedName("subject_scores")
private HashMap<String, Integer> mSubjectScores = new LinkedHashMap<>();
...
public void addSubjectScore(Subject subject, int score) {
mSubjectScores.put(subject.name(), score);
}
...
}
将EnumMap
换成LinkedHashMap
和HashMap
都可以成功解析。
后记
好记性不如烂笔头。把每次用过的知识点总结一下记录下来,就不用每次需要使用的时候去百度或者Google(没准搜出来的都是广告)。
参考
[1] Gson全解析(上)-Gson基础
[2] Gson User Guide