如何識別一個字符串是否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; // 長度比索引+1
103                          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                      #endregion
361                      break;
362                  case  '   ':
363                  case  ' \r ':
364                  case  ' \n ': // [ "a",\r\n{} ]
365                  case  ' \0 ':
366                  case  ' \t ':
367                      if (cs.keyStart <=  0 && cs.valueStart <=  0// cs.jsonStart && 
368                     {
369                          return  true; // 跳過空格。
370                     }
371                      break;
372                  default// 值開頭。。
373                      if (c ==  ' \\ '// 轉義符號
374                     {
375                          if (cs.escapeChar)
376                         {
377                             cs.escapeChar =  false;
378                         }
379                          else
380                         {
381                             cs.escapeChar =  true;
382                              return  true;
383                         }
384                     }
385                      else
386                     {
387                         cs.escapeChar =  false;
388                     }
389                      if (cs.jsonStart || cs.arrayStart)  //  Json 或數組開始了。
390                     {
391                          if (cs.keyStart <=  0 && cs.state ==  0)
392                         {
393                             cs.keyStart =  1; // 無引號的
394                         }
395                          else  if (cs.valueStart <=  0 && cs.state ==  1 && cs.jsonStart) // 只有Json開始才有值。
396                         {
397                             cs.valueStart =  1; // 無引號的
398                         }
399                     }
400                      break;
401             }
402              return  false;
403         }
404     }
405 }
View Code

補充內容:

發現本文訪問量比較高,以上的源碼在后期又有所更新,所以放出最新源碼所在的地址:

https://github.com/cyq1162/cyqdata/blob/master/Tool/JsonSplit.cs


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM