您好,登錄后才能下訂單哦!
這篇文章運用簡單易懂的例子給大家介紹Java 如何實現手動解析不帶引號的JSON字符串,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
項目中遇到了一批不帶引號的類JSON格式的字符串:
{Name:Heal,Age:20,Tag:[Coding,Reading]}
需要將其解析成JSON對象, 然后插入到Elasticsearch中, 當作Object類型的對象存儲起來.
在對比了阿里的FastJson、Google的Gson, 沒找到想要的功能 ( 可能是博主不夠仔細, 有了解的童學留言告訴我下呀😛), 于是就自己寫了個工具類, 用來實現此需求.
如果是帶有引號的標準JSON字符串, 可直接通過上述2種工具進行解析, 使用方法可參考:
Java - 格式化輸出JSON字符串的兩種方式
2.1 實現思路
代碼的主要思路在注釋中都有說明, 主要思路是:
(1) 借助Stack統計字符串首尾的[]、{}符號 —— []代表List, {}代表Map;
(2) 使用String#subString()方法縮減已解析的字符串;
(3) 使用遞歸解析內部的List、Map對象;
(4) 為了便于處理, 最小的key-value都解析成String類型.
需要注意的是: 要解析的字符串內部不要存在無意義的{、}、[、]符號, 否則會導致解析發生異常.
—— 暫時沒想到好的兼容方法, 有想法的童學請直接留言.**
2.2 詳細代碼
package com.healchow.util; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; /** * Java 解析不帶引號的JSON字符串 * * @author Heal Chow * @date 2019/08/13 11:36 */ public class ParseJsonStrUtils { public static void main(String[] args) { // 帶引號的字符串, 會將字符串當作key-value的一部分, 因此這類字符串推薦使用fastJson、Gson等工具轉換 // 注意: String內部不要存在無意義的{、}、[、]符號 - 暫時沒想到好的兼容方法 /*String sourceStr = "{\"_index\":\"book_shop\"," + "\"_id\":\"1\"," + "\"_source\":{" + "\"name\":\"Thinking in Java [4th Edition]\"," + "\"author\":\"[US] Bruce Eckel\"," + "\"price\":109.0,\"date\":\"2007-06-01 00:00:00\"," + "\"tags\":[\"Java\",[\"Programming\"]" + "}}";*/ // 不帶引號的字符串, 首尾多對[]、{}不影響解析 String sourceStr = "[[[{" + "{" + "Type:1," + "StoragePath:[{Name:/image/2019-08-01/15.jpeg,DeviceID:4401120000130},{ShotTime:2019-08-01 14:44:24}]," + "Width:140" + "}," + "{" + "Type:2,StoragePath:9090/pic/2019_08_01/src.jpeg," + "Inner:{DeviceID:44011200}," + "Test:[{ShotTime:2019-08-01 14:50:14}]," + "Width:5600}" + "}}]]]"; List<Map<String, Object>> jsonArray; Map<String, Object> jsonMap; Object obj = null; try { obj = parseJson(sourceStr); } catch (Exception e) { System.out.println("出錯啦: " + e.getMessage()); e.printStackTrace(); } if (obj instanceof List) { jsonArray = (List<Map<String, Object>>) obj; System.out.println("解析生成了List對象: " + jsonArray); } else if (obj instanceof Map) { jsonMap = (Map<String, Object>) obj; System.out.println("解析生成了Map對象: " + jsonMap); } else { System.out.println("需要解析的字符串既不是JSON Array, 也不符合JSON Object!\n原字符串: " + sourceStr); } } /** * 解析 Json 格式的字符串, 封裝為 List 或 Map 并返回 * 注意: (1) key 和 value 不能含有 ",", key 中不能含有 ":" —— 要分別用 "," 和 ":" 進行分隔 * (2) 要解析的字符串必須符合JSON對象的格式, 只對最外層的多層嵌套做了簡單的處理, * 復雜的如"{a:b},{x:y}"將不能完全識別 —— 正確的應該是"[{a:b},{x:y}]" * @param sourceStr 首尾被"[]"或"{}"包圍的字符串 * @return 生成的JsonObject */ public static Object parseJson(String sourceStr) throws InvalidParameterException { if (sourceStr == null || "".equals(sourceStr)) { return sourceStr; } // 判斷字符串首尾有沒有多余的、相匹配的 "[]" 和 "{}" String parsedStr = simplifyStr(sourceStr, "[", "]"); parsedStr = simplifyStr(parsedStr, "{", "}"); // 借助棧來實現 "[]" 和 "{}" 的出入 Stack<String> leftSymbolStack = new Stack<>(); Stack<String> rightSymbolStack = new Stack<>(); if ((parsedStr.startsWith("[") && parsedStr.endsWith("]")) || (parsedStr.startsWith("{") && parsedStr.endsWith("}"))) { leftSymbolStack.push(parsedStr.substring(0, 1)); rightSymbolStack.push(parsedStr.substring(parsedStr.length() - 1)); parsedStr = parsedStr.substring(1, parsedStr.length() - 1); // parsedStr 內部還可能是連續的"{{}}" parsedStr = simplifyStr(parsedStr, "{", "}"); } else { throw new InvalidParameterException("要解析的字符串中存在不匹配的'[]'或'{}', 請檢查!\n原字符串為: " + sourceStr); } // 保存解析的結果, jsonArray中可能只有String, 也可能含有Map<String, Object> List<Object> jsonArray = new ArrayList<>(); Map<String, Object> jsonMap = new HashMap<>(16); // 內部遍歷、解析 innerParseByLoop(parsedStr, leftSymbolStack, rightSymbolStack, jsonArray, jsonMap); // 判斷jsonArray是否為空 if (jsonArray.size() > 0) { return jsonArray; } else { return jsonMap; } } /** * 循環解析內部的List、Map對象 */ private static void innerParseByLoop(String parsedStr, Stack<String> leftSymbolStack, Stack<String> rightSymbolStack, List<Object> jsonArray, Map<String, Object> jsonMap) throws InvalidParameterException { if (parsedStr == null || parsedStr.equals("")) { return; } // 按照","分隔 String[] allKeyValues = parsedStr.split(","); if (allKeyValues.length > 0) { // 遍歷, 并按照":"分隔解析 out: for (String keyValue : allKeyValues) { // 如果keyValue中含有":", 說明該keyValue是List<Map>中的一個對象, 就需要確定第一個":"的位置 —— 可能存在多個":" int index = keyValue.indexOf(":"); if (index > 0) { // 判斷key是否仍然以"{"或"["開始, 如果是, 則壓棧 String key = keyValue.substring(0, index); while (key.startsWith("[") || key.startsWith("{")) { leftSymbolStack.push(key.substring(0, 1)); // 解析過的串要一直跟進 parsedStr = parsedStr.substring(1); key = key.substring(1); } // 判讀和value是否以"["開頭 —— 又是一個 List 對象 —— 遞歸解析 String value = keyValue.substring(index + 1); if (value.startsWith("[")) { int innerIndex = parsedStr.indexOf("]"); List<Object> innerList = (List<Object>) parseJson(parsedStr.substring(key.length() + 1, innerIndex + 1)); jsonMap.put(key, innerList); // 清除最后的"]", 并判斷是否存在"," parsedStr = parsedStr.substring(innerIndex + 1); if (parsedStr.indexOf(",") == 0) { parsedStr = parsedStr.substring(1); } // 此內部存在對象, 內部的","已經解析完畢了, 要修正按照","切割的字符串數組, 并繼續遍歷 innerParseByLoop(parsedStr, leftSymbolStack, rightSymbolStack, jsonArray, jsonMap); break; } // 判讀和value是否以 "{" 開頭 —— 又是一個 Map 對象 —— 遞歸解析 else if (value.startsWith("{")) { int innerIndex = parsedStr.indexOf("}"); Map<String, Object> innerMap = (Map<String, Object>) parseJson(parsedStr.substring(key.length() + 1, innerIndex + 1)); jsonMap.put(key, innerMap); // 清除最后的"}", 并判斷是否存在"," parsedStr = parsedStr.substring(innerIndex + 1); if (parsedStr.indexOf(",") == 0) { parsedStr = parsedStr.substring(1); } // 此內部存在對象, 內部的","已經解析完畢了, 要修正按照","切割的字符串數組, 并繼續遍歷 innerParseByLoop(parsedStr, leftSymbolStack, rightSymbolStack, jsonArray, jsonMap); break; } // 最后判斷value尾部是否含有 "]" 或 "}" else { while (value.endsWith("]") || value.endsWith("}")) { // 最右側的字符 String right = value.substring(value.length() - 1); // 此時棧頂元素 String top = leftSymbolStack.peek(); // 如果有相匹配的, 則彈棧, 否則忽略 if (("}".equals(right) && "{".equals(top)) || ("]".equals(right) && "[".equals(top))) { leftSymbolStack.pop(); value = value.substring(0, value.length() - 1); jsonMap.put(key, value); // 清除最后的"}", 并判斷是否存在"," parsedStr = parsedStr.substring(key.length() + 1 + value.length() + 1); if (parsedStr.indexOf(",") == 0) { parsedStr = parsedStr.substring(1); } // 解析完成了一個對象, 則將該元素添加到List中, 并創建新的對象 jsonArray.add(jsonMap); jsonMap = new HashMap<>(16); // 繼續進行外層的解析 continue out; } // 如果都不匹配, 則有可能是源字符串的最后一個符號 else { rightSymbolStack.push(right); value = value.substring(0, value.length() - 1); } } jsonMap.put(key, value); int length = key.length() + value.length() + 2; if (parsedStr.length() > length) { parsedStr = parsedStr.substring(length); } else { parsedStr = ""; } } } // 如果keyValue中不含":", 說明該keyValue只是List<String>中的一個串, 而非List<Map>中的一個Map, 則直接將其添加到List中即可 else { jsonArray.add(keyValue); } } // 遍歷結束, 處理最后的符號問題 —— 判斷左右棧是否匹配 while (!leftSymbolStack.empty()) { if (leftSymbolStack.peek().equals("{") && parsedStr.equals("}")) { leftSymbolStack.pop(); } if (!rightSymbolStack.empty()) { if (leftSymbolStack.peek().equals("{") && rightSymbolStack.peek().equals("}")) { leftSymbolStack.pop(); rightSymbolStack.pop(); } else if (leftSymbolStack.peek().equals("[") && rightSymbolStack.peek().equals("]")) { leftSymbolStack.pop(); rightSymbolStack.pop(); } else { throw new InvalidParameterException("傳入的字符串中不能被解析成JSON對象!\n原字符串為: " + parsedStr); } } } } } /** * 判斷字符串首尾有沒有多余的、相匹配的 "[]" 和 "{}", 對其進行簡化 */ private static String simplifyStr(String sourceStr, String firstSymbol, String lastSymbol) { while (sourceStr.startsWith(firstSymbol) && sourceStr.endsWith(lastSymbol)) { String second = sourceStr.substring(1, 2); // 如果第二個仍然是"["或"{", 再判斷倒數第二個是不是"]"或"}" —— 說明長度至少為3, 不會發生 IndexOutOfBoundsException if (second.equals(firstSymbol)) { String penultimate = sourceStr.substring(sourceStr.length() - 2, sourceStr.length() - 1); if (penultimate.equals(lastSymbol)) { // 縮短要解析的串 sourceStr = sourceStr.substring(1, sourceStr.length() - 1); } else { break; } } else { break; } } return sourceStr; } }
2.3 測試樣例
(1) 帶引號的測試:
// 測試字符串: String sourceStr = "{\"_index\":\"book_shop\"," + "\"_id\":\"1\"," + "\"_source\":{" + "\"name\":\"Thinking in Java [4th Edition]\"," + "\"author\":\"[US] Bruce Eckel\"," + "\"price\":109.0,\"date\":\"2007-06-01 00:00:00\"," + "\"tags\":[\"Java\",[\"Programming\"]" + "}}";
解析結果為:
(2) 不帶引號的測試:
// 測試字符串: String sourceStr = "[[[{" + "{" + "Type:1," + "StoragePath:[{Name:/image/2019-08-01/15.jpeg,DeviceID:4401120000130},{ShotTime:2019-08-01 14:44:24}]," + "Width:140" + "}," + "{" + "Type:2,StoragePath:9090/pic/2019_08_01/src.jpeg," + "Inner:{DeviceID:44011200}," + "Test:[{ShotTime:2019-08-01 14:50:14}]," + "Width:5600}" + "}}]]]";
解析結果為:
補充知識:將key名不帶雙引號的JSON字符串轉換成JSON對象的方法
根據json.org上面的描述,JSON對象是由對象成員組成,而成員是由key-value鍵值組成。
key值是一個字符串:
字符串由Unicode字符組成,用雙引號包圍,用反斜杠轉義。可以是單個字符。用法跟C或Java里的字符串的用法相似。
但是,在現實應用中,很少有程序員知道JSON里的key需要用雙引號包圍,因為大多數的瀏覽器里并不需要使用雙引號。所以,為什么多此一舉要多寫兩個雙引號呢?
規范的例子:
{
"keyName" : 34
}
不規范的例子:
{
keyName : 34
}
雖然在瀏覽器里使用不規范的、不使用雙引號的寫法在瀏覽器里不會出現問題,但并不代表你可以在其它地方不會遇到問題,比如,你有一個字符串:
//字符串格式
'{ keyName : 34 }'
你想把它轉換成JSON對象。把JSON字符串轉換成JSON對象,需要使用 JSON.parse()方法,對于上面的這種key名上不帶雙引號的的JSON字符串,使用JSON.parse()解析時會報錯,無法解析。這就成了一個很麻煩的問題。所以說,盡量使用規范的預防還是有好處的,盡管大多數時候你不會遇到問題。
那么,對于key名不帶雙引號的JSON字符串,如何將它轉換成JSON對象呢?
最直接的方法是手工給key名加上雙引號。
如果你不像手工添加,可以使用函數全文搜索追加雙引號,比如下面的這段代碼:
json_string.replace(/(s*?{s*?|s*?,s*?)(['"])?([a-zA-Z0-9]+)(['"])?:/g, '$1"$3":');
eval('var json = new Object(' + json_string + ')');
最后,最簡單的一種方法是直接用eval()運行它:
var obj = eval('(' + invalid_json + ')');
但這樣執行時,你需要理解執行的代碼是什么,因為如果它里面含有一些惡意程序,你這樣直接運行很可能引起安全問題。
關于Java 如何實現手動解析不帶引號的JSON字符串就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。