1. 程式人生 > >如何識別一個字串是否Json格式

如何識別一個字串是否Json格式

前言:

距離上一篇文章,又過去一個多月了,近些時間,工作依舊很忙碌,除了管理方面的事,程式碼方面主要折騰三個事:

1:開發框架(一整套基於配置型的開發體系框架)

2:CYQ.Data 資料層框架(持續的更新,最近也加入了Sybase的支援)

3:工作流流程圖設計器。

由於這三個方面都涉及到Json,所以就談談這些天在Json上花下的心思。

關於造輪子:

很多人對於造輪子都會有自己的看法,這裡提一下個人的觀點: 

個人認為:

1:首要是要具備造輪子的能力,然後再討論造不造與浪不浪、輪子與時間的問題。

2:造輪子的、寫文章的,永遠比使用輪子的、看文章的,多經歷了一些、多思考一些、多知道一些。

所以,別嫌造輪子折騰,雖然的確很折騰,不是有那麼句:生命在於折騰,除了瞎折騰。

PS:本來文章是寫Json常用的功能互動那塊相關的知識,所以才有這一段。 

不多扯了,扯多了都是蛋,還是迴歸正題吧。 

如何識別一個字串是不是Json。

網上搜了一下,找到兩三個坑人的答案:

A:Js識別,Eval一下,成功就是,失敗就掛。

B:C#識別,判斷開始和結束符號:{}或[] 

C:用正則表示式判斷。

上面ABC答案都純忽悠,只要認真一下,都不靠譜了。

經過我的研究,發現這是有很有挑戰性的課題:

Json需要分析的情況,比想象的要多,舉一個不太簡單的Json:

[1,{"a":2},\r\n{"a":{}}, {"a":[]},{"a":[{}]},{"{[a":"\"2,:3,"a":33}]"}]

從上面這個Json中,就可以看出需要分析的有:

1:陣列和Json陣列。 

2:鍵與值(無引號、雙引號)的識別

3:無限級值巢狀(陣列巢狀、Json巢狀)

4:7個關鍵符號[{,:"}]。

5:轉義符號、空格、換行、回車處理。 

回顧早些年寫的JsonHelper

還記得CYQ.Data裡JsonHelper的最初版本,僅處理了只有一級Json的簡單情況,那時候分析Json就靠以下兩種方法:

1:Split 分隔。

2:迴圈 indexOf 識別。

雖然偷工減料,投機取巧,但只要限定使用環境和條件、好在夠用,也夠簡單。

當然了,現在情況變了,把限定的環境和條件去除後,事實上,要分析起來就沒那麼簡單了。

故事一開始,思考了三天三夜

由於放開了條件,需要考慮無限級遞迴的,於是看似Split和IndexOf這種方式已經不奏效了。

字串的分析方法看似需要改朝換代了,但我仍給Split和IndexOf尋求最後的機會。

經過層層思考與分析,發經沒折了,只有祭出終極必殺招了。

終極大招:遍歷字元,記錄狀態 

一個萬能的解決方法,就是遍歷每個字元,然後記錄這個字元前後左右上下東南西北中發白各種狀態,再根據狀態來識別下一個字元的動作。

1:首先有一個記錄字元狀態的類,如下圖:

 

這個字元狀態的記錄類,我前後不斷調整了N天,才終於感覺好像OK了。 

2:接下來是字元的狀態設定,根據不同的關鍵字,設定狀態,如下圖:

 

這是個漫長不斷除錯的過程,很折騰人。

3:一個可以不斷遞迴Json的函式,如下圖:

4:一個可以識別語法錯誤的函式:

5:最後是一個給外部的呼叫方法:

 

總結:

雖然本文是關於識別Json格式,實際上,它已經是Json解析類的核心,用它可以演化出Json的各種應用,有機會再介紹了。

事實上, 一開始是原打算寫Json與Xml互轉那一塊的,寫文的意原來自最近一週折騰工作流的流程設計器那一塊:

 

從Xml出來到前端成為Json,編輯完後回去又要轉回原始格式的Xml存檔,所以在Xml和Json間,必須有一套協議,這些,大概是時間不夠,所以臨時變了一個題目。 

關於Json的線上解析,以及Json和Xml和互轉,臨時我開了個域名 :tool.cyqdata.com,僅方便自己使用。

夜已深,該閉眼去夢裡的世界旅遊了。 

最後是本文的原始碼:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Text;
  4 
  5 namespace CYQ.Data.Tool
  6 {
  7     ///<summary>  8 /// 分隔Json字串為字典集合。
  9 ///</summary> 10     internal class JsonSplit
 11     {
 12         private static bool IsJsonStart(ref string json)
 13         {
 14             if (!string.IsNullOrEmpty(json))
 15             {
 16                 json = json.Trim('\r''\n''');
 17                 if (json.Length > 1)
 18                 {
 19                     char s = json[0];
 20                     char e = json[json.Length - 1];
 21                     return (s == '{' && e == '}') || (s == '[' && e == ']');
 22                 }
 23             }
 24             return false;
 25         }
 26         internal static bool IsJson(string json)
 27         {
 28             int errIndex;
 29             return IsJson(json, out errIndex);
 30         }
 31         internal static bool IsJson(string json, out int errIndex)
 32         {
 33             errIndex = 0;
 34             if (IsJsonStart(ref json))
 35             {
 36                 CharState cs = new CharState();
 37                 char c;
 38                 for (int i = 0; i < json.Length; i++)
 39                 {
 40                     c = json[i];
 41                     if (SetCharState(c, ref cs) && cs.childrenStart)//設定關鍵符號狀態。 42                     {
 43                         string item = json.Substring(i);
 44                         int err;
 45                         int length = GetValueLength(item, trueout err);
 46                         cs.childrenStart = false;
 47                         if (err > 0)
 48                         {
 49                             errIndex = i + err;
 50                             return false;
 51                         }
 52                         i = i + length - 1;
 53                     }
 54                     if (cs.isError)
 55                     {
 56                         errIndex = i;
 57                         return false;
 58                     }
 59                 }
 60 
 61                 return !cs.arrayStart && !cs.jsonStart;
 62             }
 63             return false;
 64         }
 65        
 66         ///<summary> 67 /// 獲取值的長度(當Json值巢狀以"{"或"["開頭時)
 68 ///</summary> 69         private static int GetValueLength(string json, bool breakOnErr, out int errIndex)
 70         {
 71             errIndex = 0;
 72             int len = 0;
 73             if (!string.IsNullOrEmpty(json))
 74             {
 75                 CharState cs = new CharState();
 76                 char c;
 77                 for (int i = 0; i < json.Length; i++)
 78                 {
 79                     c = json[i];
 80                     if (!SetCharState(c, ref cs))//設定關鍵符號狀態。 81                     {
 82                         if (!cs.jsonStart && !cs.arrayStart)//json結束,又不是陣列,則退出。 83                         {
 84                             break;
 85                         }
 86                     }
 87                     else if (cs.childrenStart)//正常字元,值狀態下。 88                     {
 89                         int length = GetValueLength(json.Substring(i), breakOnErr, out errIndex);//遞迴子值,返回一個長度。。。 90                         cs.childrenStart = false;
 91                         cs.valueStart = 0;
 92                         //cs.state = 0; 93                         i = i + length - 1;
 94                     }
 95                     if (breakOnErr && cs.isError)
 96                     {
 97                         errIndex = i;
 98                         return i;
 99                     }
100                     if (!cs.jsonStart && !cs.arrayStart)//記錄當前結束位置。101                     {
102                         len = i + 1;//長度比索引+1103                         break;
104                     }
105                 }
106             }
107             return len;
108         }
109         ///<summary>110 /// 字元狀態
111 ///</summary>112         private class CharState
113         {
114             internal bool jsonStart = false;//以 "{"開始了...115             internal bool setDicValue = false;// 可以設定字典值了。116             internal bool escapeChar = false;//以"\"轉義符號開始了117             ///<summary>118 /// 陣列開始【僅第一開頭才算】,值巢狀的以【childrenStart】來標識。
119 ///</summary>120             internal bool arrayStart = false;//以"[" 符號開始了121             internal bool childrenStart = false;//子級巢狀開始了。122             ///<summary>123 /// 【0 初始狀態,或 遇到“,”逗號】;【1 遇到“:”冒號】
124 ///</summary>125             internal int state = 0;
126 
127             ///<summary>128 /// 【-1 取值結束】【0 未開始】【1 無引號開始】【2 單引號開始】【3 雙引號開始】
129 ///</summary>130             internal int keyStart = 0;
131             ///<summary>132 /// 【-1 取值結束】【0 未開始】【1 無引號開始】【2 單引號開始】【3 雙引號開始】
133 ///</summary>134             internal int valueStart = 0;
135             internal bool isError = false;//是否語法錯誤。136 
137             internal void CheckIsError(char c)//只當成一級處理(因為GetLength會遞迴到每一個子項處理)138             {
139                 if (keyStart > 1 || valueStart > 1)
140                 {
141                     return;
142                 }
143                 //示例 ["aa",{"bbbb":123,"fff","ddd"}] 144                 switch (c)
145                 {
146                     case '{'://[{ "[{A}]":[{"[{B}]":3,"m":"C"}]}]147                         isError = jsonStart && state == 0;//重複開始錯誤 同時不是值處理。148                         break;
149                     case '}':
150                         isError = !jsonStart || (keyStart != 0 && state == 0);//重複結束錯誤 或者 提前結束{"aa"}。正常的有{}151                         break;
152                     case '[':
153                         isError = arrayStart && state == 0;//重複開始錯誤154                         break;
155                     case ']':
156                         isError = !arrayStart || jsonStart;//重複開始錯誤 或者 Json 未結束157                         break;
158                     case '"':
159                     case '\'':
160                         isError = !(jsonStart || arrayStart); //json 或陣列開始。161                         if (!isError)
162                         {
163                             //重複開始 [""",{"" "}]164                             isError = (state == 0 && keyStart == -1) || (state == 1 && valueStart == -1);
165                         }
166                         if (!isError && arrayStart && !jsonStart && c == '\'')//['aa',{}]167                         {
168                             isError = true;
169                         }
170                         break;
171                     case ':':
172                         isError = !jsonStart || state == 1;//重複出現。173                         break;
174                     case ',':
175                         isError = !(jsonStart || arrayStart); //json 或陣列開始。176                         if (!isError)
177                         {
178                             if (jsonStart)
179                             {
180                                 isError = state == 0 || (state == 1 && valueStart > 1);//重複出現。181                             }
182                             else if (arrayStart)//["aa,] [,]  [{},{}]183                             {
184                                 isError = keyStart == 0 && !setDicValue;
185                             }
186                         }
187                         break;
188                     case '':
189                     case '\r':
190                     case '\n'://[ "a",\r\n{} ]191                     case '\0':
192                     case '\t':
193                         break;
194                     default//值開頭。。195                         isError = (!jsonStart && !arrayStart) || (state == 0 && keyStart == -1) || (valueStart == -1 && state == 1);//
196                         break;
197                 }
198                 //if (isError)
199 //{
200 201 //}202             }
203         }
204         ///<summary>205 /// 設定字元狀態(返回true則為關鍵詞,返回false則當為普通字元處理)
206 ///</summary>207         private static bool SetCharState(char c, ref CharState cs)
208         {
209             cs.CheckIsError(c);
210             switch (c)
211             {
212                 case '{'://[{ "[{A}]":[{"[{B}]":3,"m":"C"}]}]213                     #region 大括號
214                     if (cs.keyStart <= 0 && cs.valueStart <= 0)
215                     {
216                         cs.keyStart = 0;
217                         cs.valueStart = 0;
218                         if (cs.jsonStart && cs.state == 1)
219                         {
220                             cs.childrenStart = true;
221                         }
222                         else
223                         {
224                             cs.state = 0;
225                         }
226                         cs.jsonStart = true;//開始。227                         return true;
228                     }
229                     #endregion
230                     break;
231                 case '}':
232                     #region 大括號結束
233                     if (cs.keyStart <= 0 && cs.valueStart < 2 && cs.jsonStart)
234                     {
235                         cs.jsonStart = false;//正常結束。236                         cs.state = 0;
237                         cs.keyStart = 0;
238                         cs.valueStart = 0;
239                         cs.setDicValue = true;
240                         return true;
241                     }
242                     // cs.isError = !cs.jsonStart && cs.state == 0;243                     #endregion
244                     break;
245                 case '[':
246                     #region 中括號開始
247                     if (!cs.jsonStart)
248                     {
249                         cs.arrayStart = true;
250                         return true;
251                     }
252                     else if (cs.jsonStart && cs.state == 1)
253                     {
254                         cs.childrenStart = true;
255                         return true;
256                     }
257                     #endregion
258                     break;
259                 case ']':
260                     #region 中括號結束
261                     if (cs.arrayStart && !cs.jsonStart && cs.keyStart <= 2 && cs.valueStart <= 0)//[{},333]//這樣結束。262                     {
263                         cs.keyStart = 0;
264                         cs.valueStart = 0;
265                         cs.arrayStart = false;
266                         return true;
267                     }
268                     #endregion
269                     break;
270                 case '"':
271                 case '\'':
272                     #region 引號
273                     if (cs.jsonStart || cs.arrayStart)
274                     {
275                         if (cs.state == 0)//key階段,有可能是陣列["aa",{}]276                         {
277                             if (cs.keyStart <= 0)
278                             {
279                                 cs.keyStart = (c == '"' ? 3 : 2);
280                                 return true;
281                             }
282                             else if ((cs.keyStart == 2 && c == '\'') || (cs.keyStart == 3 && c == '"'))
283                             {
284                                 if (!cs.escapeChar)
285                                 {
286                                     cs.keyStart = -1;
287                                     return true;
288                                 }
289                                 else
290                                 {
291                                     cs.escapeChar = false;
292                                 }
293                             }
294                         }
295                         else if (cs.state == 1 && cs.jsonStart)//值階段必須是Json開始了。296                         {
297                             if (cs.valueStart <= 0)
298                             {
299                                 cs.valueStart = (c == '"' ? 3 : 2);
300                                 return true;
301                             }
302                             else if ((cs.valueStart == 2 && c == '\'') || (cs.valueStart == 3 && c == '"'))
303                             {
304                                 if (!cs.escapeChar)
305                                 {
306                                     cs.valueStart = -1;
307                                     return true;
308                                 }
309                                 else
310                                 {
311                                     cs.escapeChar = false;
312                                 }
313                             }
314 
315                         }
316                     }
317                     #endregion
318                     break;
319                 case ':':
320                     #region 冒號
321                     if (cs.jsonStart && cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 0)
322                     {
323                         if (cs.keyStart == 1)
324                         {
325                             cs.keyStart = -1;
326                         }
327                         cs.state = 1;
328                         return true;
329                     }
330                     // cs.isError = !cs.jsonStart || (cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 1);331                     #endregion
332                     break;
333                 case ',':
334                     #region 逗號 //["aa",{aa:12,}]
335 
336                     if (cs.jsonStart)
337                     {
338                         if (cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 1)
339                         {
340                             cs.state = 0;
341                             cs.keyStart = 0;
342                             cs.valueStart = 0;
343                             //if (cs.valueStart == 1)
344 //{
345 //    cs.valueStart = 0;
346 //}347                             cs.setDicValue = true;
348                             return true;
349                         }
350                     }
351                     else if (cs.arrayStart && cs.keyStart <= 2)
352                     {
353                         cs.keyStart = 0;
354                         //if (cs.keyStart == 1)
355 //{
356 //    cs.keyStart = -1;
357 //}358                         return true;
359                     }
360