由於目前項目是以TCP通訊爲主,使用自研協議解析工具來解析自定義的傳輸協議。json
因此並無引入第三方JSON解析庫,目前仍是依賴原生JSON解析庫進行解析。bash
使用如下代碼構建一個JSONObject,並將其toString。app
public static String toJson(JsEvent jsEvent) {
JSONObject jo = new JSONObject();
try {
jo.put(TYPE, jsEvent.getType());
jo.put(IDENTIFIER, jsEvent.getIdentifier());
String method = jsEvent.getMethod();
jo.put(METHOD, method);
Map<String,Object> joParams = new HashMap<>();
joParams.put("code", code);
joParams.put("msg", msg);
Map<String, Object> data = new HashMap<>();
data.put("token", "authToken");
joParams.put("data", data);
jo.put("result", new JSONObject(joParams));
} catch (Exception e) {
....
}
return jo.toString();
}複製代碼
以上代碼能夠在絕大部分設備上獲得符合JSON協議標準的stringide
{
"type": "Callback",
"identifier": "1",
"method": "getToken",
"result": {
"msg": "",
"code": 0,
"data": {
"token": "tgmj2o8rs9n4s24psq18bu6k6y0tycpf"
}
}
}複製代碼
但在sdk <= Android 4.3的手機上,卻獲得如下JSON string函數
{
"type": "Callback",
"identifier": "1",
"method": "getToken",
"result": {
"msg": "",
"code": 0,
"data": "{token=tgmj2o8rs9n4s24psq18bu6k6y0tycpf}"
}
}複製代碼
能夠看出來工具
由於org.json是系統庫,因而很容易就想到是sdk版本實現差別致使的。源碼分析
從代碼和結果上分析ui
在有問題的手機上獲得的結果中能夠看出來,一直到第二層的JSONObject,都是能夠正常解析的。this
對應到代碼上spa
jo.put("result", new JSONObject(joParams)); 複製代碼
能夠看到,
但爲何第三層
Map<String, Object> data = new HashMap<>();
data.put("token", "authToken");
joParams.put("data", data);複製代碼
使用這樣的方式去構建JSONObject,在Android 4.3以上能夠正常解析,但4.3如下卻不行呢?
通過以上的分析,咱們能夠大概把焦點鎖定在JSONObject(Map params)
這個構造方法中 和 JSONObject#toString
這兩個方法中。
從JSONObject源碼分析
簡單闡述一下JSONObject#toString
的工做原理:
調用writeTo
方法去遍歷JSONObject
中的nameValuePairs
,將key->value
處理成字符串的形式。在處理value
時有兩種狀況:
若是是JSONArray
或JSONObject
,會遞歸調用writeTo
方法,繼續進行處理。
若是非以上兩個特殊類型,都會將其轉換成string(調用Object#toString
或者其餘方法),而且append到結果中。
而後再來看一下表現正常的 sdk 8.0 的JSONObject(Map params)
方法實現。
public JSONObject(Map copyFrom) {
this();
Map<?, ?> contentsTyped = (Map<?, ?>) copyFrom;
for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) {
String key = (String) entry.getKey();
if (key == null) {
throw new NullPointerException("key == null");
}
nameValuePairs.put(key, wrap(entry.getValue()));
}
}
複製代碼
表現異常的sdk 4.2.2的JSONObject(Map params)
方法實現。
public JSONObject(Map copyFrom) {
this();
Map<?, ?> contentsTyped = (Map<?, ?>) copyFrom;
for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) {
String key = (String) entry.getKey();
if (key == null) {
throw new NullPointerException("key == null");
}
nameValuePairs.put(key, entry.getValue());
}
}複製代碼
能夠很明顯的看出來,sdk 8.0在遍歷map時,調用wrap函數對value進行了處理。對Collection
、array
、Map
這幾種集合容器作了處理,使用明確的JSONObject
或JSONArray
來代替它們。而sdk 4.2.2中並無作這樣的處理。
在以前分析的JSONObject#toString
工做原理的基礎上,再回到最開始的那段代碼中,由於第三層的key-value集合是一個Map,因此,將會調用Map#toString
方法來生成value。
這就是爲何那段代碼會在不一樣版本的平臺上表現出差別的緣由啦~
直接在上層代碼作修改,使用明確的JSONObject代替Collection
、array
、Map
這幾種集合容器。
模仿sdk 8.0的處理方式,重載JSONObject(Map params)
這個構造方法。