面對Java解析Json字符串的需求,有不少開源工具供咱們選擇,如google的Gson、阿里巴巴的fastJson。在網上能找到大量的文章講解這些工具的使用方法。我也是參考這些文章封裝了本身的Json解析工具類。這個工具類能夠完成Json字符串和對應的實體類對象間的相互轉換。用着挺方便的,因此咱們之間一直相安無事。直到有一天我遇到了一個新的Json字符串解析需求node
將「nodesInService」的值從下面的Json字符串中解析出來。apache
{json
"beans" : [ {數組
"name" :"Hadoop:service=NameNode,name=BlockStats",函數
"modelerType" :"org.apache.hadoop.hdfs.server",工具
"StorageTypeStats" : [ {oop
"key" : "DISK",google
"value" : {spa
"blockPoolUsed" : 26618108614,server
"capacityRemaining" : 204199376575,
"capacityTotal" : 280360910848,
"capacityUsed" : 26618108614,
"nodesInService" : 4
}
} ]
} ]
}
按照一向的作法,須要根據Json字符串的結構定義一個實體類,而後使用Json解析工具類將這個Json字符串映射成對應實體對象,再從對象中將「nodesInService」的值讀出來。
這Json字符串看起來結構挺複雜的,數組和對象相互嵌套。此外需求只想要「nodesInService」這一個元素的值,其餘元素都不關心。此時個人懶癌無可救藥的發做了。感受爲了一個值就要:定義+映射+讀取...。有點興師動衆有木有?小題大做有木有?殺雞用了牛刀有沒有?
我只想要一個接口,把原始Json字符串和須要的元素名稱傳入,接口自動解析出元素的值返回給我。幸虧Json字符串的結構看似複雜,其實只有兩種元素類型「JSONObject」和「JSONArray」。無論這兩種類型數據如何嵌套,只要知道元素類型和名稱就能解析出來。因此理論上只要從根元素開始,告訴接口每一級元素的類型和名稱,程序就能按圖索驥將最後一個元素的值提取出來。
依據以上思路制定了一個元素描述規則:
1) Json元素描述規則:
「元素類型:元素名稱」,使用「:」分隔。
2) 類型描述規則:
A:JSONArray
E:JSONObject
3) 元素描述項之間使用「,」間隔。根元素到目標元素描述項從左向右排列。例如上面的「nodesInService」元素的描述項字符串是「A:beans,A:StorageTypeStats,E:value,E:nodesInService」。
使用Gson解析工具實現Json字符串解析函數源碼以下:
/**
* json字符串解析函數
*
* @param targetNames
* 目標名字,由「目標類型:目標名稱」,以「,」間隔
* 目標類型: A:JSONArray
* E:JSONObject,如{"name":"value"}
* 例如: "A:beans,A:StorageTypeStats,E:value,E:nodesInService"
* @param rawJson
* Json字符串
* @return
* 最後target對應的值
*/
public static String parseJson(String targetNames, String rawJson) {
String retValue = null;
String[] names = targetNames.split(",");
if (names.length <= 0) {
System.out.println("Error: parameter \"targetNames\" is invalid!");
return retValue;
}
try {
JsonParser parser = new JsonParser(); //建立json解析器
JsonObject json = (JsonObject) parser.parse(rawJson); //建立jsonObject對象
JsonArray jarray = null;
for (int i=0; i < names.length -1; i++) {
System.out.println(names[i]);
String[] subName = names[i].split(":");
if (subName.length != 2) {
System.out.println("Error: parameter \""+ names[i] + "\" is invalid!");
return retValue;
}
String type = subName[0];
String jName = subName[1];
switch (type) {
case "A":
if (null == jarray && null != json) {
jarray = json.getAsJsonArray(jName);
json = null;
}
else if (null != jarray && null == json) {
JsonElement element = jarray.get(0);
JsonObject json1 = element.getAsJsonObject();
jarray = json1.getAsJsonArray(jName);
json = null;
}
else {
System.out.println("Error: parse json string failed!");
return retValue;
}
break;
case "E":
if (null == jarray && null != json) {
JsonElement element = json.get(jName);
json = element.getAsJsonObject();
jarray = null;
}
else if (null != jarray && null == json) {
JsonElement element = jarray.get(0);
JsonObject json1 = element.getAsJsonObject();
json = json1.getAsJsonObject(jName);
jarray = null;
}
else {
System.out.println("Error: parse json string failed!");
return retValue;
}
break;
default:
System.out.println("Error: json type \"" + type + "\" is invalid!");
return retValue;
}
}
String[] subName = names[names.length-1].split(":");
if (subName.length != 2) {
System.out.println("Error: parameter \"targetNames\" is invalid!");
return retValue;
}
String jName = subName[1];
if (null != jarray && null == json) {
JsonElement ele = jarray.get(0);
if (null != ele) {
JsonObject jo = ele.getAsJsonObject();
if (null != jo) {
retValue = jo.get(jName).getAsString();
}
}
return retValue;
}
else if (null == jarray && null != json) {
retValue = json.get(jName).getAsString();
}
else {
System.out.println("Error: parse \"" + jName + "\" failed!");
}
}catch (JsonIOException e) {
e.printStackTrace();
} catch (JsonSyntaxException e) {
e.printStackTrace();
}
return retValue;
}
該函數的功能能夠知足這個特定場景的需求,只要將從根元素到目標元素的描述信息字符串和原始Json字符串傳入,函數能夠直接將目標元素的值解析出來,返回給調用者。使用方式比之實體類映射模式更簡潔一些。
細心的小夥伴看了源碼可能說「你這個函數不行啊,我要的目標元素是整型或浮點型時,你給我一個String返回值算怎麼回事?」,其實不區分元素類型統一返回String類型是爲了簡化描述規則和函數實現的複雜度。相較在描述規則中增長類型描述,由調用者作一次類型轉換更簡單一些。
另外一位小夥伴又說了「你這個函數有bug, 裏面的魔鬼數字將數組類型元素的取值限定在第一個元素上了」。沒錯,源碼確實有這個限制。形成這種現象的緣由是我遇到的需求中,全部的數組元素都只有一個值。因此個人懶癌又發做了一次,沒有實現更多的預設場景,例如,解析出數組中第N個元素的值、全部元素值或者與另外一個元素相關聯的元素值。這些場景的需求,使用類似的思路也是能夠實現的。