json-lib-2.4-jdk15 的疑似 bug

使用 json-lib 时发现了比较诡异的现象,分析半天没有找到原因。最后因时间关系,直接替换成 org.json 解决了。疑似json-lib 有 bug,Mark 一个,以后再看。

最近尝试封装某网络 API ,提供接口给上层调用。该 API 使用 json 发送数据。一开始图方便,本想直接用 String 拼接了事,后来实际一写,发现 json 结构有三层,比较复杂,而且有动态增删改字段的需求,改为老老实实构建 JSONObject ,发送前转成 String 。

不像 JavaScript 和 Python 那样有官方的 json 实现,Java 的 json 实现山头林立。我粗略数了一下 json.org 官网上都有25个之多!我也不要什么复杂的功能,只要

  1. 根据需要建立标准 json 对象结构
  2. 方便在 对象 和 字符串 之间互相转换

够了。

大概扫一眼,net.sf.json-liborg.json 都够用,刚好这两个包接口极为接近。项目别的地方已经引用了 json-lib,干脆就它。

注:代码是对某网络 API的封装,它的API都是公开信息。敏感信息我已经消去。

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
@Override
public boolean setPredefinedKeyValue(String key, Object value)
throws Exception {
System.out.println("key:" + key + ", value:" + value);
if (ROOT_KEYS.contains(key)) {
rootJson.put(key, value);
} else if (PAYLOAD_KEYS.contains(key)) {
JSONObject payloadJson = null;
if (rootJson.has("payload")) {
payloadJson = rootJson.getJSONObject("payload");
System.out.println("payload exists: " + payloadJson.toString());
} else {
payloadJson = new JSONObject();
rootJson.put("payload", payloadJson);
System.out.println("new payload.");
}
payloadJson.put(key, value);
} else if (BODY_KEYS.contains(key)) {
System.out.println("Enter body ==========");
JSONObject bodyJson = null;
JSONObject payloadJson = null;
if (rootJson.has("payload")) {
payloadJson = rootJson.getJSONObject("payload");
System.out.println("payload exists: " + payloadJson.toString());
} else {
payloadJson = new JSONObject();
rootJson.put("payload", payloadJson);
System.out.println("new payload and add. root: " + rootJson.toString());
}
System.out.println("payload before has(body) : " + payloadJson.toString());
if (payloadJson.has("body")) {
bodyJson = payloadJson.getJSONObject("body");
System.out.println("body exists: " + bodyJson.toString());
} else {
bodyJson = new JSONObject();
payloadJson.put("body", bodyJson);
System.out.println("new body and add. payload: " + payloadJson.toString());
}
System.out.println("body before put: " + bodyJson.toString());
bodyJson.put(key, value);
System.out.println("body after put: " + bodyJson.toString());
System.out.println("Exit body ==========");
} else if (POLICY_KEYS.contains(key)) {
// ......
} else {
// ......
}
return true;
}

以下是 json-lib 的log

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
key:appkey, value:********************************
key:type, value:customizedcast
key:alias, value:***********
key:alias_type, value:phone
key:ticker, value:测试
Enter body ==========
new payload and add. root: {"alias_type":"phone","payload":{},"alias":"***********","appkey":"********************************","type":"customizedcast"}
payload before has(body) : {}
new body and add. payload: {"body":{}}
body before put: {}
body after put: {"ticker":"测试"}
Exit body ==========
key:title, value:测试标题
Enter body ==========
payload exists: {}
payload before has(body) : {}
new body and add. payload: {"body":{}}
body before put: {}
body after put: {"title":"测试标题"}
Exit body ==========
key:text, value:测试内容
Enter body ==========
payload exists: {}
payload before has(body) : {}
new body and add. payload: {"body":{}}
body before put: {}
body after put: {"text":"测试内容"}
Exit body ==========
key:after_open, value:go_app
Enter body ==========
payload exists: {"body":{"text":"测试内容"}}
payload before has(body) : {"body":{"text":"测试内容"}}
body exists: {"text":"测试内容"}
body before put: {"text":"测试内容"}
body after put: {"text":"测试内容","after_open":"go_app"}
Exit body ==========
key:display_type, value:notification
payload exists: {"body":{"text":"测试内容","after_open":"go_app"}}
key:production_mode, value:true
key:timestamp, value:**********
body: {"appkey":"********************************","type":"customizedcast","alias":"***********","alias_type":"phone","payload":{"display_type":"notification","body":{"text":"测试内容","after_open":"go_app"}},"production_mode":"true","timestamp":"**********"}

问题在第 15 行开始出现,明明前面刚添加完 body ,马上就不见了;然而最后添加 after_open 时,包含 text 的 body 却保留下来了! 而且每次运行结果都一样,并不随机。

改为 org.json 后的 log (因两个包接口基本兼容,只是修改 import 的包,还有把初始化 JSONObject.fromObject(str) 改为 new JSONObject(str) 而已,上面代码一点没动,就不重新贴了)

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
key:appkey, value:********************************
key:type, value:customizedcast
key:alias, value:***********
key:alias_type, value:phone
key:ticker, value:测试
Enter body ==========
new payload and add. root: {"alias_type":"phone","payload":{},"alias":"***********","appkey":"********************************","type":"customizedcast"}
payload before has(body) : {}
new body and add. payload: {"body":{}}
body before put: {}
body after put: {"ticker":"测试"}
Exit body ==========
key:title, value:测试标题
Enter body ==========
payload exists: {"body":{"ticker":"测试"}}
payload before has(body) : {"body":{"ticker":"测试"}}
body exists: {"ticker":"测试"}
body before put: {"ticker":"测试"}
body after put: {"ticker":"测试","title":"测试标题"}
Exit body ==========
key:text, value:测试内容
Enter body ==========
payload exists: {"body":{"ticker":"测试","title":"测试标题"}}
payload before has(body) : {"body":{"ticker":"测试","title":"测试标题"}}
body exists: {"ticker":"测试","title":"测试标题"}
body before put: {"ticker":"测试","title":"测试标题"}
body after put: {"ticker":"测试","text":"测试内容","title":"测试标题"}
Exit body ==========
key:after_open, value:go_app
Enter body ==========
payload exists: {"body":{"ticker":"测试","text":"测试内容","title":"测试标题"}}
payload before has(body) : {"body":{"ticker":"测试","text":"测试内容","title":"测试标题"}}
body exists: {"ticker":"测试","text":"测试内容","title":"测试标题"}
body before put: {"ticker":"测试","text":"测试内容","title":"测试标题"}
body after put: {"after_open":"go_app","ticker":"测试","text":"测试内容","title":"测试标题"}
Exit body ==========
key:display_type, value:notification
payload exists: {"body":{"after_open":"go_app","ticker":"测试","text":"测试内容","title":"测试标题"}}
key:production_mode, value:true
key:timestamp, value:**********
body: {"alias_type":"phone","payload":{"display_type":"notification","body":{"after_open":"go_app","ticker":"测试","text":"测试内容","title":"测试标题"}},"alias":"***********","appkey":"********************************","type":"customizedcast","production_mode":"true","timestamp":"**********"}

一个可以明显发现的细节是,json-lib 保持了添加顺序,而 org.json 没有。我看过 json-lib 源码是用 ListOrderedMap(看的最新代码,没找我引用的版本,不过估计差别不会太大)。

然而这并没有什么用,内容都丢了,保持顺序有毛线用。


知识共享 “署名-非商业性使用-相同方式共享” 4.0 (CC BY-NC-SA 4.0)”许可协议
本文为本人原创,采用知识共享 “署名-非商业性使用-相同方式共享” 4.0 (CC BY-NC-SA 4.0)”许可协议进行许可。
本作品可自由复制、传播及基于本作品进行演绎创作。如有以上需要,请留言告知,在文章开头明显位置加上署名(Jayce Chant)、原链接及许可协议信息,并明确指出修改(如有),不得用于商业用途。谢谢合作。
详情请点击查看协议具体内容。