標籤:
識別Json字串並分隔成Map集合前言:
最近又看了點Java的知識,於是想著把CYQ.Data V5遷移到Java版本。
過程發現坑很多,理論上看大部分很相似,實踐上代碼寫起來發現大部分都要重新思考方案。
遇到的C#轉Java的一些小問題:1:C#的ref:這個參數的主要意圖是:
就算方法內部重新對參數重新(new)賦值,也能保證外部的參數指向新的值。
Java木有了,不知道新的方案是什嗎?還是說只能避免方法內部重新(new)賦值?
2:C#的out:這個參數的主要意圖是:
處理多值返回的問題。
Java木有了,只能改返回數組了。
3:C#的SqlDbType和DbType
其實我也沒想過C#為什麼還要搞個DbType(用在DbParameter參數上使用)
Java:涉入不深,不知道用啥代替?
4:C#的DBNull.Value
Java:好像木有這東東,用啥代替?
5:C#的ChangeType 通用類型轉換
Java:涉入不深,不知道用啥代替?
6:C#的反射Type有屬性可以識別是否泛型(IsGenericType)和Nullable.GetUnderlyingType(t)
Java:在Class裡找不到泛型判斷,替代方案是?
7:發現Map集合的大多數預設都對key進行了排序,最後發現只有這個才預設沒排序:LinkedHashMap8:關於程式碼摺疊功能:
C#裡用#region和#endregion。
Java裡要下個外掛程式,然後改成//region和//endregion。
9:Java木有Internal修飾符,木有Jar包內使用的修飾符,真心不友好。10:其它的主是一些大小寫和方法的改了。
好了,先小小總結到這裡,下面上Java代碼,主要有3個類:
1:實現代碼(內部實作類別):CharState
package cyq.data.tool;/// <summary>/// 字元狀態/// </summary>class CharState{ boolean jsonStart = false;//以 "{"開始了... boolean setDicValue = false;// 可以設定字典值了。 boolean escapeChar = false;//以"\"轉義符號開始了 /// <summary> /// 數組開始【僅第一開頭才算】,值嵌套的以【childrenStart】來標識。 /// </summary> boolean arrayStart = false;//以"[" 符號開始了 boolean childrenStart = false;//子級嵌套開始了。 /// <summary> /// 【0 取名稱中】;【1 取值中】 /// </summary> int state = -1; /// <summary> /// 【-1 未初始化】【0 未開始】【1 無引號開始】【2 單引號開始】【3 雙引號開始】 /// </summary> int keyStart = -1; /// <summary> /// 【-1 未初始化】【0 未開始】【1 無引號開始】【2 單引號開始】【3 雙引號開始】 /// </summary> int valueStart = -1; boolean isError = false;//是否語法錯誤。 void CheckIsError(char c)//只當成一級處理(因為GetLength會遞迴到每一個子項處理) { switch (c) { case ‘{‘://[{ "[{A}]":[{"[{B}]":3,"m":"C"}]}] isError = jsonStart && state == 0;//重複開始錯誤 同時不是值處理。 break; case ‘}‘: isError = !jsonStart || (keyStart > -1 && state == 0);//重複結束錯誤 或者 提前結束。 break; case ‘[‘: isError = arrayStart && state == 0;//重複開始錯誤 break; case ‘]‘: isError = !arrayStart;//重複開始錯誤 break; case ‘"‘: case ‘\‘‘: isError = !jsonStart;//未開始Json break; case ‘:‘: isError = !jsonStart || (jsonStart && keyStart < 2 && valueStart < 2 && state == 1);//未開始Json 同時 只能處理在取值之前。 break; case ‘,‘: isError = (!jsonStart && !arrayStart) || (jsonStart && keyStart < 2 && valueStart < 2 && state == 0);//未開始Json 同時 只能處理在取值之後。 break; default: //值開頭。。 isError = !jsonStart || (keyStart == 0 && valueStart == 0 && state == 0);// break; } //if (isError) //{ //} }}
2:實現代碼(內部實作類別):JsonSplit
PS:Log這個類的代碼可以先注釋屏蔽。
package cyq.data.tool;import java.util.*;import cyq.data.Log;public class JsonSplit{ public static boolean IsJson(String json) { return IsJsonAndBackIndex(json)==-1; } public static int IsJsonAndBackIndex(String json) { if(json==null || json.isEmpty() || json.length()==1) { return 0; } if ((json.charAt(0) == ‘{‘ && json.charAt(json.length() - 1) == ‘}‘) || (json.charAt(0)== ‘[‘ && json.charAt(json.length() - 1)== ‘]‘)) { int errIndex = 0; CharState cs = new CharState(); char c; for (int i = 0; i < json.length(); i++) { c = json.charAt(i); if (SetCharState(c, cs) && cs.childrenStart)//設定關鍵符號狀態。 { String item = json.substring(i); int[] v=GetValueLength(item, true); int length = v[0]; int err=v[1]; cs.childrenStart = false; if (err > 0) { errIndex = i + err; return errIndex; } i = i + length - 1; } if (cs.isError) { errIndex = i; return errIndex; } } if( !cs.arrayStart && !cs.jsonStart) { return -1; } return errIndex; } else { return 0; } } static List<LinkedHashMap<String, String>> Split(String json) { List<LinkedHashMap<String, String>> result = new ArrayList<LinkedHashMap<String, String>>(); if (json!=null && !json.isEmpty()) { LinkedHashMap<String, String> dic = new LinkedHashMap<String, String>(); String key = ""; StringBuilder value = new StringBuilder(); CharState cs = new CharState(); try { //region 核心邏輯 char c; for (int i = 0; i < json.length(); i++) { c = json.charAt(i); if (!SetCharState(c, cs))//設定關鍵符號狀態。 { if (cs.jsonStart)//Json進行中。。。 { if (cs.keyStart > 0) { key += c; } else if (cs.valueStart > 0) { value.append(c); //value += c; } } else if (!cs.arrayStart)//json結束,又不是數組,則退出。 { break; } } else if (cs.childrenStart)//正常字元,值狀態下。 { String item = json.substring(i); int[] v= GetValueLength(item, false); int length =v[0]; //int temp=v[1]; //value = item.SubString(0, length); value.setLength(0); value.append(item.substring(0, length)); cs.childrenStart = false; cs.valueStart = 0; //cs.state = 0; cs.setDicValue = true; i = i + length - 1; } if (cs.setDicValue)//設定索引值對。 { if (key!=null && !key.isEmpty() && !dic.containsKey(key)) { //if (value != String.Empty) //{ boolean isNull = json.charAt(i - 5) == ‘:‘ && json.charAt(i) != ‘"‘ && value.length() == 4 && value.toString() == "null"; if (isNull) { value.setLength(0); } dic.put(key, value.toString()); //} } cs.setDicValue = false; key = ""; value.setLength(0); } if (!cs.jsonStart && dic.size() > 0) { result.add(dic); if (cs.arrayStart)//處理數組。 { dic = new LinkedHashMap<String, String>(); } } } //endregion } catch (Exception err) { Log.WriteLogToTxt(err); } finally { key = null; value.setLength(0); value = null; } } return result; } /// <summary> /// 擷取值的長度(當Json值嵌套以"{"或"["開頭時) /// </summary> private static int[] GetValueLength(String json, boolean breakOnErr) { int errIndex = 0; int len = 0; if (json!=null && !json.isEmpty()) { CharState cs = new CharState(); char c; for (int i = 0; i < json.length(); i++) { c = json.charAt(i); if (!SetCharState(c, cs))//設定關鍵符號狀態。 { if (!cs.jsonStart && !cs.arrayStart)//json結束,又不是數組,則退出。 { break; } } else if (cs.childrenStart)//正常字元,值狀態下。 { int[] v=GetValueLength(json.substring(i), breakOnErr);//遞迴子值,返回一個長度。。。 int length = v[0]; errIndex=v[1]; cs.childrenStart = false; cs.valueStart = 0; //cs.state = 0; i = i + length - 1; } if (breakOnErr && cs.isError) { errIndex = i; break; } if (!cs.jsonStart && !cs.arrayStart)//記錄當前結束位置。 { len = i + 1;//長度比索引+1 break; } } } int[] valueAndErrIndex=new int[2]; valueAndErrIndex[0]=len; valueAndErrIndex[1]=errIndex; return valueAndErrIndex; } /// <summary> /// 設定字元狀態(返回true則為關鍵詞,返回false則當為一般字元處理) /// </summary> private static boolean SetCharState(char c, CharState cs) { switch (c) { case ‘{‘://[{ "[{A}]":[{"[{B}]":3,"m":"C"}]}] if (cs.keyStart <= 0 && cs.valueStart <= 0) { cs.CheckIsError(c); if (cs.jsonStart && cs.state == 1) { cs.valueStart = 0; cs.childrenStart = true; } else { cs.state = 0; } cs.jsonStart = true;//開始。 return true; } break; case ‘}‘: if (cs.keyStart <= 0 && cs.valueStart < 2) { cs.CheckIsError(c); if (cs.jsonStart) { cs.jsonStart = false;//正常結束。 cs.valueStart = 0; cs.setDicValue = true; } return true; } // cs.isError = !cs.jsonStart && cs.state == 0; break; case ‘[‘: if (!cs.jsonStart) { cs.CheckIsError(c); cs.arrayStart = true; return true; } else if (cs.jsonStart && cs.state == 1 && cs.valueStart < 2) { cs.CheckIsError(c); //cs.valueStart = 1; cs.childrenStart = true; return true; } break; case ‘]‘: if (!cs.jsonStart && cs.keyStart <= 0 && cs.valueStart <= 0) { cs.CheckIsError(c); if (cs.arrayStart)// && !cs.childrenStart { cs.arrayStart = false; } return true; } break; case ‘"‘: case ‘\‘‘: cs.CheckIsError(c); if (cs.jsonStart) { if (cs.state == 0)//key階段 { cs.keyStart = (cs.keyStart <= 0 ? (c == ‘"‘ ? 3 : 2) : 0); return true; } else if (cs.state == 1)//值階段 { if (cs.valueStart <= 0) { cs.valueStart = (c == ‘"‘ ? 3 : 2); return true; } else if ((cs.valueStart == 2 && c == ‘\‘‘) || (cs.valueStart == 3 && c == ‘"‘)) { if (!cs.escapeChar) { cs.valueStart = 0; return true; } else { cs.escapeChar = false; } } } } break; case ‘:‘: cs.CheckIsError(c); if (cs.jsonStart && cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 0) { cs.keyStart = 0; cs.state = 1; return true; } // cs.isError = !cs.jsonStart || (cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 1); break; case ‘,‘: cs.CheckIsError(c); if (cs.jsonStart && cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 1) { cs.state = 0; cs.valueStart = 0; cs.setDicValue = true; return true; } else if (cs.arrayStart && !cs.jsonStart) { return true; } break; case ‘ ‘: case ‘\r‘: case ‘\n‘: if (cs.jsonStart && cs.keyStart <= 0 && cs.valueStart <= 0) { return true;//跳過空格。 } break; default: //值開頭。。 cs.CheckIsError(c); if (c == ‘\\‘) //轉義符號 { if (cs.escapeChar) { cs.escapeChar = false; } else { cs.escapeChar = true; return true; } } else { cs.escapeChar = false; } if (cs.jsonStart) { if (cs.keyStart <= 0 && cs.state <= 0) { cs.keyStart = 1;//無引號的 } else if (cs.valueStart <= 0 && cs.state == 1) { cs.valueStart = 1;//無引號的 } } break; } return false; }}
3:實現代碼(外部調用類):JsonHelper
package cyq.data.tool;import java.util.*;public class JsonHelper { /// <summary> /// 檢測是否Json格式的字串 /// </summary> /// <param name="json">要檢測的字串</param> public static boolean IsJson(String json) { return JsonSplit.IsJson(json); } /// <summary> /// 將Json分隔成索引值對。 /// </summary> public static Map<String, String> Split(String json) { List<LinkedHashMap<String, String>> result = JsonSplit.Split(json); if (result != null && result.size() > 0) { return result.get(0); } return null; } /// <summary> /// 將Json 數組分隔成多個索引值對。 /// </summary> public static List<LinkedHashMap<String, String>> SplitArray(String jsonArray) { if (jsonArray==null || jsonArray.isEmpty()) { return null; } jsonArray = jsonArray.trim(); return JsonSplit.Split(jsonArray); }}
4:實現代碼(單元測試類):JsonTest
PS:標籤Key沒用引號,所以識別是false,但Split方法是相容不strict 模式的,所以還能分成Map。
總結:
總體而言,雖然Java很原始,C#很進階,但技術這東西,走向一個極端後,也得學學大閘蟹,得橫著走。
只有兩手抓,兩手都硬的時候,才不會被技術所孤立。。。
識別Json字串並分隔成Map集合