开源库—Gson处理超大型Json文本

对于超大型的Json文本,如果直接使用Gson的fromJson(String json, Class<T> classOfT)方法将可能导致内存问题,特别是当该Json文本中存在大型数组时,因为数组是通过复制(copy)的方法扩容的,这样就很容易导致内存不足而报异常。要解决这个问题,可以使用流(stream)来处理。

具体在使用Google的Gson库时,即选择使用JsonReader来处理。它的具体做法是一个一个读取Json文本中的元素,然后写入对象。

下面是一个使用示例,先创建User和Address两个POJO。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Address {


private String province;

private String city;

private String district;

private String StreetAddr;

private String phone;


//get和set方法省略
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class User {

private long id;

private Date createTime;

private String name;

private String avator;

private List<Address> receiveAddrs;


//get和set方法省略
}

具体如何使用JsonReader,可以参考如下:

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
86
87
88
89
90
91
92
93
94
95
96
97
98
public class BigJsonTest {

private static Gson gson = new GsonBuilder().setPrettyPrinting().create();

public static void main(String[] args) throws IOException {
User user = new User();
user.setId(302919489L);
user.setName("Paul");
user.setCreateTime(new Date());
user.setAvator("https://guangzhou.myqcloud.com/avator.jpg");

List<Address> addresses = new ArrayList<>();

Address address1 = new Address("广东省", "深圳市", "南山区");
address1.setStreetAddr("留仙大道5588号");
address1.setPhone("15011112222");
addresses.add(address1);

Address address2 = new Address("北京市", "北京市", "海淀区");
address2.setStreetAddr("中关村路");
address2.setPhone("13800008888");
addresses.add(address2);

user.setReceiveAddrs(addresses);

String jsonText = new Gson().toJson(user);
User userFromJson = parse(jsonText);
System.out.println(gson.toJson(userFromJson));
}

private static User parse(String jsonText) throws IOException {
StringReader stringReader = new StringReader(jsonText);
JsonReader jsonReader = new JsonReader(stringReader);
User workerHistory = new User();
jsonReader.beginObject();
try {
while (jsonReader.hasNext()) {
String key = jsonReader.nextName();
if ("id".equals(key)) {
workerHistory.setId(jsonReader.nextLong());
} else if ("name".equals(key)) {
workerHistory.setName(jsonReader.nextString());
} else if ("receiveAddrs".equals(key)) {
List<Address> addresses = readAddressList(jsonReader);
workerHistory.setReceiveAddrs(addresses);
} else {
jsonReader.skipValue();
}
}
jsonReader.endObject();
return workerHistory;
} catch (Exception e) {
e.printStackTrace();
} finally {
stringReader.close();
try {
jsonReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}


private static List<Address> readAddressList(JsonReader jsonReader) throws IOException {
List<Address> registCouriers = new ArrayList<>(8);
jsonReader.beginArray();
while (jsonReader.hasNext()) {
Address courier = new Address();
registCouriers.add(courier);
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String key = jsonReader.nextName();
switch (key) {
case "province":
courier.setProvince(jsonReader.nextString());
break;
case "city":
courier.setCity(jsonReader.nextString());
break;
case "district":
courier.setDistrict(jsonReader.nextString());
break;
default:
jsonReader.skipValue();
break;

}
}
jsonReader.endObject();
}
jsonReader.endArray();
return registCouriers;
}


}

注意:jsonReader的skipValue()在上面的使用是必要的,因为如果没有skipValue()方法,上面循环中jsonReader.hasNext()方法会读取到Json键值对中的value来当做key,那样就肯定不对了。

https://developer.android.com/reference/android/util/JsonReader

------ 本文完 ------