Gson TypeToken特性实战

也许你和我一样有这样的经历,后台的json字段格式总是各种变化,一个字段,既可能是一个普通的String,也可能是Object,一会是int Array,一会是Object Array,甚至是Map。原因不外乎,历史遗漏代码啊,进度紧张没时间磨平差异啊,或者干脆就是无法控制的因素等。今天我就简单总结下,该如何用Gson库的TypeArray特性,优雅的处理这种糟糕格式的json。

年前和团队一起赶项目进度,加上搬家什么的各种杂事,好久没静下心写篇文章了,转眼2017年,趁着年前进度收尾,不是特别忙碌的功夫,我争取吧这段时间中遇到的技术问题,总结下来,基本都以实战为主,短篇。

事件篇–可怕的Json格式

首先我们来看这样一串json:

1
2
3
4

{
"Person":"Stephen Curry"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

public class Data {

@SerializedName("Person")
private String Person;

public String getPerson() {
return Person;
}

public void setPerson(String Person) {
this.Person = Person;
}
}

很简单对不对,但是还有其它几种可能情况:

也许30号球衣那位,在后台看来就是库里,所以这会变成Person字段变成数值了

1
2
3
4

{
"Person": 30
}

你看,后台贴心的放上了库里的生日,Person字段也自然而然变成了对象

1
2
3
4
5
6
{
"Person": {
"name": "Stephen Curry",
"birthday": "1988-03-14"
}
}

库里,你不是一个人

1
2
3
4
5
6
7
8
9
10
11
12
{
"Person": [
{
"name": "Stephen Curry",
"birthday": "1988-03-14"
},
{
"name": "Klay Thompson",
"birthday": "1990-02-08"
}
]
}

也许人多了,忘却你生日,只记住你的姓名

1
2
3
4
5
6
{
"Person": [
"Stephen Curry",
"Klay Thompson"
]
}

后台也许有时候觉得你需要一个key,方便在人群中找到你。

1
2
3
4
5
6
7
8
9
10
{
"Person": {
"Stephen Curry": {
"birthday": "1988-03-14"
},
"Klay Thompson": {
"birthday": "1990-02-08"
}
}
}

对方不想和你说话,并向你丢了如上格式的json

疑难篇–Gson

我们知道,Gson默认的json转Object的是这样做的:

1
2
Gson gson = new Gson();
Person person = gson.fromJson(jsonString, Person.class);

Gson可以通过注册GsonBuilder()的registerTypeAdapter的方法,手动注入某一个类的解析规则,

再看Gson中几种Json格式所对应的类的关系,看图一目了然:

我们只要针对每一层JsonElement判断具体的类型并手动创建对象和赋值,就可以了。

解决篇–自定义具体类的解析方式

不过俗话说得好,不会投三分的Android开发不是好的产品,我们怎么能被这样恶心的接口轻易地恶心倒呢,来看Gson库给我们提供的泛型支持工具 TypeToken

google/gson

首先定义一个合适的类结构,表示Person,这里仅作参考

1
2
3
4
5
6
7
8
public class Persons {
public String name;
public int number;
public String birthday;
public List<String> personNames = new ArrayList<>();
public List<Person> persons = new ArrayList<>();
public HashMap<String, Person> personMap = new HashMap<>();
}
1
2
3
4
5
6
public class Person {
@SerializedName("name")
public String name;
@SerializedName("birthday")
public String birthday;
}
1
2
3
4
public class Data {
@SerializedName("Person")
public Persons persons;
}

接着,定义对应的TypeToken和JsonDeserializer,针对该类手动处理Json解析

1
2
public static final Type TypePersons = new TypeToken<Persons>() {
}.getType();
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public static JsonDeserializer<Persons> personsJsonDeserializer = new JsonDeserializer<Persons>() {
@Override
public Persons deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
Persons persons = new Persons();
if (jsonElement.isJsonObject()) {
Gson gson = new Gson();
JsonObject jsonObject = jsonElement.getAsJsonObject();
JsonElement personElement = jsonObject.get("Person");
if (personElement.isJsonPrimitive()) {
JsonPrimitive jsonPrimitive = jsonElement.getAsJsonPrimitive();
if (jsonPrimitive.isString()) {
//"Person":"Stephen Curry"
persons.name = jsonPrimitive.getAsString();
} else if (jsonPrimitive.isNumber()) {
//"Person": 30
persons.number = jsonPrimitive.getAsInt();
}
} else if (personElement.isJsonObject()) {
JsonObject jsonObject2 = jsonElement.getAsJsonObject();
Set<Map.Entry<String, JsonElement>> entrySet = jsonObject2.entrySet();
HashMap<String, Person> personMap = new HashMap<>();
for (Map.Entry<String, JsonElement> entry : entrySet) {
if ("person".equals(entry.getKey())) {
/**
* "Person": {
" name": "Stephen Curry",
" birthday": "1988-03-14"
* }
*/
persons.person = gson.fromJson(jsonElement, Person.class);
break;
} else {
/**
* "Person": {
* "Stephen Curry": {
* "birthday": "1988-03-14"
* },
* "Klay Thompson": {
* "birthday": "1990-02-08"
* }
* }
*/
personMap.put(entry.getKey(), gson.fromJson(entry.getValue(), Person.class));
}
}
persons.personMap = personMap;
}
} else if (jsonElement.isJsonArray()) {
Gson gson = new Gson();
JsonArray jsonArray = jsonElement.getAsJsonArray();
List<String> personNames = new ArrayList<>();
List<Person> personList = new ArrayList<>();
if (jsonArray != null && jsonArray.size() > 0) {
for (JsonElement arrayElement : jsonArray) {
if (arrayElement.isJsonPrimitive()) {
/**
* "Person": [
* "Stephen Curry",
* "Klay Thompson"
* ]
*/
personNames.add(arrayElement.getAsString());
} else if (arrayElement.isJsonObject()) {
/**
* "Person": [
* {
* "name": "Stephen Curry",
* "birthday": "1988-03-14"
* },
* {
* "name": "Klay Thompson",
* "birthday": "1990-02-08"
* }
* ]
*/
personList.add(gson.fromJson(jsonElement, Person.class));
}
}
}
persons.personNames = personNames;
persons.persons = personList;
}
return persons;
}
};
1
2
3
4
Gson gson = new GsonBuilder()
.registerTypeAdapter(TypeArea, personsJsonDeserializer)
.create();
return gson.fromJson(jsonElement, Persons.class);

代码应该写得很清楚了,以上。