激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C# - C#實現JSON解析器MojoUnityJson功能(簡單且高效)

C#實現JSON解析器MojoUnityJson功能(簡單且高效)

2022-02-19 15:34scottcgi C#

MojoUnityJson 是使用C#實現的JSON解析器 ,算法思路來自于游戲引擎Mojoc的C語言實現 Json.h。這篇文章主要介紹了C#實現JSON解析器MojoUnityJson的方法,需要的朋友可以參考下

MojoUnityJson 是使用C#實現的JSON解析器 ,算法思路來自于游戲引擎Mojoc的C語言實現 Json.h 。借助C#的類庫,可以比C的實現更加的簡單和全面,尤其是處理Unicode Code(\u開頭)字符的解析,C#的StringBuilder本身就支持了UnicodeCodePoint。

MojoUnityJson使用遞歸下降的解析模式,核心解析代碼只有450行(去掉空行可能只有300多行),支持標準的JSON格式。算法實現力求簡潔明了,用最直接最快速的方法達到目的,沒有復雜的概念和模式。除了解析JSON,還提供了一組方便直觀的API來訪問JSON數據,整體實現只有一個文件,僅依賴 System.Collections.Generic System.Text System 三個命名空間,MojoUnityJson可以很容易的嵌入到其它項目里使用。

本文主要介紹一下,超級簡單又高效,并且看一眼就完全明白的解析算法,幾乎可以原封不動的復制粘貼成其它語言版本的實現。

保存上下文信息

使用一個簡單的結構體,用來在解析的過程中,傳遞一些上下文數據。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private struct Data
{
 // 需要解析的JSON字符串
 public string  json;
 // 當前JSON字符串解析的位置索引
 public int   index;
 // 緩存一個StringBuilder,用來摳出JSON的一段字符。
 public StringBuilder sb;
 public Data(string json, int index)
 {
  this.json = json;
  this.index = index;
  this.sb = new StringBuilder();
 }
}

抽象JSON的值

我們把JSON的值抽象成以下幾個類型:

?
1
2
3
4
5
6
7
8
9
public enum JsonType
 {
  Object,
  Array,
  String,
  Number,
  Bool,
  Null,
 }

整體解析步驟

?
1
2
3
4
5
6
7
8
9
10
// 解析 JsonValue
private static JsonValue ParseValue(ref Data data);
// 解析 JsonObject
private static JsonValue ParseObject(ref Data data);
// 解析 JsonArray
private static JsonValue ParseArray(ref Data data);
// 解析 string
private static JsonValue ParseString(ref Data data);
// 解析 number
private static JsonValue ParseNumber(ref Data data)

這就是全部的解析流程,在ParseValue中會根據字符判斷類型,分別調用下面幾個不同的解析函數。JsonValue就對應一個JSON的值,它有一個JsonType代表了這個值的類型。這是一個遞歸的過程,在ParseValue,ParseObject和ParseArray過程中,會遞歸的調用ParseValue。JSON一定是始于一個,Object或Array,當這個最頂層的值解析完畢的時候,整個JSON也就解析完成了。

解析空白字符

解析過程中,會有很多為了格式化存在的空白字符,需要剔除這些,才能獲得有信息的字符,這是一個重復的過程,需要一個函數統一處理。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static void SkipWhiteSpace(ref Data data)
{
 while (true)
 {
  switch (data.json[data.index])
  {
   case ' ' :
   case '\t':
   case '\n':
   case '\r':
    data.index++; // 每次消耗一個字符,就向后推進JSON的索引
    continue;
  }
  break;
 }
}

解析JsonValue

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
private static JsonValue ParseValue(ref Data data)
{
 // 跳過空白字符
 SkipWhiteSpace(ref data);
 var c = data.json[data.index];
 switch (c)
 {
  case '{':
   // 表示Object
   return ParseObject(ref data);
  case '[':
   // 表示Array
   return ParseArray (ref data);
  case '"':
   // 表示string
   return ParseString(ref data);
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
  case '-':
   // 表示數值
   return ParseNumber(ref data);
  case 'f': // 表示可能是false
   if
    (
     data.json[data.index + 1] == 'a' &&
     data.json[data.index + 2] == 'l' &&
     data.json[data.index + 3] == 's' &&
     data.json[data.index + 4] == 'e'
    )
   {
    data.index += 5;
    // 表示是false
    return new JsonValue(JsonType.Bool, false);
   }
   break;
  case 't': // 表示可能是true
   if
    (
     data.json[data.index + 1] == 'r' &&
     data.json[data.index + 2] == 'u' &&
     data.json[data.index + 3] == 'e'
    )
   {
    data.index += 4;
    // 表示是true
    return new JsonValue(JsonType.Bool, true);
   }
   break;
  case 'n': // 表示可能是null
   if
    (
     data.json[data.index + 1] == 'u' &&
     data.json[data.index + 2] == 'l' &&
     data.json[data.index + 3] == 'l'
    )
   {
    data.index += 4;
    // 表示可能是null
    return new JsonValue(JsonType.Null, null);
   }
   break;
 }
 // 不能處理了
 throw new Exception(string.Format("Json ParseValue error on char '{0}' index in '{1}' ", c, data.index));
}
  • ParseValue是解析的主入口,代表著解析JsonValue這個抽象的JSON值,其真實的類型在解析的過程中逐漸具體化。
  • 在剝離掉空白字符之后,就可以很容易的通過單個字符,就判斷出其可能的數值類型,而不需要向前或向后檢索。
  • true,false,null 這幾個固定的類型,直接就處理掉了,而其它稍微復雜的類型需要使用函數來處理。
  • 這里沒有使用if else,而是大量使用了case,是為了提高效率,減少判斷次數。

解析JsonObject

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
private static JsonValue ParseObject(ref Data data)
{
 // Object 對應 C#的Dictionary
 var jsonObject = new Dictionary<string, JsonValue>(JsonObjectInitCapacity);
 // skip '{'
 data.index++;
 do
 {
  // 跳過空白字符
  SkipWhiteSpace(ref data);
  if (data.json[data.index] == '}')
  {
   // 空的Object, "{}"
   break;
  }
  DebugTool.Assert
  (
   data.json[data.index] == '"',
   "Json ParseObject error, char '{0}' should be '\"' ",
   data.json[data.index]
  );
  // skip '"'
  data.index++;
  var start = data.index;
  // 解析Object的key值
  while (true)
  {
   var c = data.json[data.index++];
   switch (c)
   {
    case '"':
     // check end '"'
     break;
    case '\\':
     // skip escaped quotes
     data.index++;
     continue;
    default:
     continue;
   }
   // already skip the end '"'
   break;
  }
  // get object key string
  // 扣出key字符串
  var key = data.json.Substring(start, data.index - start - 1);
  // 跳過空白
  SkipWhiteSpace(ref data);
  DebugTool.Assert
  (
   data.json[data.index] == ':',
   "Json ParseObject error, after key = {0}, char '{1}' should be ':' ",
   key,
   data.json[data.index]
  );
  // skip ':'
  data.index++;
  // set JsonObject key and value
  // 遞歸的調用ParseValue獲得Object的value值
  jsonObject.Add(key, ParseValue(ref data));
  // 跳過空白
  SkipWhiteSpace(ref data);
  if (data.json[data.index] == ',')
  {
   // Object的下一對KV
   data.index++ ;    
  }
  else
  {
   // 跳過空白
   SkipWhiteSpace(ref data);
   DebugTool.Assert
   (
    data.json[data.index] == '}',
    "Json ParseObject error, after key = {0}, char '{1}' should be '{2}' ",
    key,
    data.json[data.index],
    '}'
   );
   break;
  }
 }
 while (true);
 // skip '}' and return after '}'
 data.index++;
 return new JsonValue(JsonType.Object, jsonObject);
}

JsonObject類型就簡單的對應C#的Dictionary,value是JsonValue類型。當解析完成后,value的類型就是確定的了。

JsonValue是遞歸的調用ParseValue來處理的,其類型可能是JsonType枚舉的任意類型。

解析JsonArray

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private static JsonValue ParseArray(ref Data data)
{
 // JsonArray 對應 List
 var jsonArray = new List<JsonValue>(JsonArrayInitCapacity);
 // skip '['
 data.index++;
 do
 {
  // 跳過空白
  SkipWhiteSpace(ref data);
  if (data.json[data.index] == ']')
  {
   // 空 "[]"
   break;
  }
  // add JsonArray item
  // 遞歸處理List每個元素
  jsonArray.Add(ParseValue(ref data));
  // 跳過空白
  SkipWhiteSpace(ref data);
  if (data.json[data.index] == ',')
  {
   // 解析下一個元素
   data.index++;
  }
  else
  {
   // 跳過空白
   SkipWhiteSpace(ref data);
   DebugTool.Assert
   (
    data.json[data.index] == ']',
    "Json ParseArray error, char '{0}' should be ']' ",
    data.json[data.index]
   );
   break;
  }
 }
 while (true);
 // skip ']'
 data.index++;
 return new JsonValue(JsonType.Array, jsonArray);
}

JsonArray類型就簡單的對應C#的List,element是JsonValue類型。當解析完成后,element的類型就是確定的了。

JsonValue是遞歸的調用ParseValue來處理的,其類型可能是JsonType枚舉的任意類型。

解析string

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
private static JsonValue ParseString(ref Data data)
{
 // skip '"'
 data.index++;
 var start = data.index;
 string str;
 // 處理字符串
 while (true)
 {
  switch (data.json[data.index++])
  {
   case '"': // 字符串結束
    // check end '"'     
    if (data.sb.Length == 0)
    {
     // 沒有使用StringBuilder,直接摳出字符串
     str = data.json.Substring(start, data.index - start - 1);
    }
    else
    {
     // 有特殊字符在StringBuilder
     str = data.sb.Append(data.json, start, data.index - start - 1).ToString();
     // clear for next string
     // 清空字符,供下次使用
     data.sb.Length = 0;
    }
    break;
   case '\\':
    {
     // check escaped char
     var escapedIndex = data.index;
     char c;
     // 處理各種轉義字符
     switch (data.json[data.index++])
     {
      case '"':
       c = '"';
       break;
      case '\'':
       c = '\'';
       break;
      case '\\':
       c = '\\';
       break;
      case '/':
       c = '/';
       break;
      case 'n':
       c = '\n';
       break;
      case 'r':
       c = '\r';
       break;
      case 't':
       c = '\t';
       break;
      case 'u':
       // 計算unicode字符的碼點
       c = GetUnicodeCodePoint
        (
         data.json[data.index],
         data.json[data.index + 1],
         data.json[data.index + 2],
         data.json[data.index + 3]
        );
       // skip code point
       data.index += 4;
       break;
      default:
       // not support just add in pre string
       continue;
     }
     // add pre string and escaped char
     // 特殊處理的字符和正常的字符,一起放入StringBuilder
     data.sb.Append(data.json, start, escapedIndex - start - 1).Append(c);
     // update pre string start index
     start = data.index;
     continue;
    }
   default:
    continue;
  }
  // already skip the end '"'
  break;
 }
 return new JsonValue(JsonType.String, str);
}

處理字符串麻煩的地方在于,轉義字符需要特殊處理,都這轉義字符就會直接顯示而不能展示特殊的作用。好在StringBuilder功能非常強大,提供處理各種情況的接口。

解析Unicode字符

在JSON中,Unicode字符是以\u開頭跟隨4個碼點組成的轉義字符。碼點在StringBuilder的Append重載函數中是直接支持的。所以,我們只要把\u后面的4個字符,轉換成碼點傳遞給Append就可以了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/// <summary>
/// Get the unicode code point.
/// </summary>
private static char GetUnicodeCodePoint(char c1, char c2, char c3, char c4)
{
 // 把\u后面的4個char轉換成碼點,注意這里需要是char類型,才能被Append正確處理。
 // 4個char轉換為int后,映射到16進制的高位到低位,然后相加得到碼點。
 return (char)
   (
    UnicodeCharToInt(c1) * 0x1000 +
    UnicodeCharToInt(c2) * 0x100 +
    UnicodeCharToInt(c3) * 0x10 +
    UnicodeCharToInt(c4)
   );
}
/// <summary>
/// Single unicode char convert to int.
/// </summary>
private static int UnicodeCharToInt(char c)
{
 // 使用switch case 減少 if else 的判斷
 switch (c)
 {
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
   return c - '0';
  case 'a':
  case 'b':
  case 'c':
  case 'd':
  case 'e':
  case 'f':
   return c - 'a' + 10;
  case 'A':
  case 'B':
  case 'C':
  case 'D':
  case 'E':
  case 'F':
   return c - 'A' + 10;
 }
 throw new Exception(string.Format("Json Unicode char '{0}' error", c));
}

解析number

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private static JsonValue ParseNumber(ref Data data)
{
 var start = data.index;
 // 收集數值字符
 while (true)
 {
  switch (data.json[++data.index])
  {
   case '0':
   case '1':
   case '2':
   case '3':
   case '4':
   case '5':
   case '6':
   case '7':
   case '8':
   case '9':
   case '-':
   case '+':
   case '.':
   case 'e':
   case 'E':
    continue;
  }
  break;
 }
 // 摳出數值字符串
 var strNum = data.json.Substring(start, data.index - start);
 float num;
 // 當成float處理,當然也可以用double
 if (float.TryParse(strNum, out num))
 {
  return new JsonValue(JsonType.Number, num);
 }
 else
 {
  throw new Exception(string.Format("Json ParseNumber error, can not parse string [{0}]", strNum));
 }
}

如何使用

只有一句話,把Json字符串解析成JsonValue對象,然后JsonValue對象包含了所有的數值。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
var jsonValue = MojoUnity.Json.Parse(jsonString);
JsonValue的訪問API
// JsonValue 當做 string
public string AsString();
// JsonValue 當做 float
public float AsFloat();
// JsonValue 當做 int
public float AsInt();
// JsonValue 當做 bool
public float AsBool();
// JsonValue 當做 null
public float IsNull();
// JsonValue 當做 Dictionary
public Dictionary<string, JsonValue> AsObject();
// JsonValue 當做 Dictionary 并根據 key 獲取 value 當做JsonValue
public JsonValue AsObjectGet(string key);
// JsonValue 當做 Dictionary 并根據 key 獲取 value 當做 Dictionary
public Dictionary<string, JsonValue> AsObjectGetObject(string key);
// JsonValue 當做 Dictionary 并根據 key 獲取 value 當做 List
public List<JsonValue> AsObjectGetArray(string key);
// JsonValue 當做 Dictionary 并根據 key 獲取 value 當做 string
public string AsObjectGetString(string key);
// JsonValue 當做 Dictionary 并根據 key 獲取 value 當做 float
public float AsObjectGetFloat(string key);
// JsonValue 當做 Dictionary 并根據 key 獲取 value 當做 int
public int AsObjectGetInt(string key);
// JsonValue 當做 Dictionary 并根據 key 獲取 value 當做 bool
public bool AsObjectGetBool(string key);
// JsonValue 當做 Dictionary 并根據 key 獲取 value 當做 null
public bool AsObjectGetIsNull(string key);
// JsonValue 當做 List
public List<JsonValue> AsArray();
// JsonValue 當做 List 并獲取 index 的 value 當做 JsonValue
public JsonValue AsArrayGet(int index);
// JsonValue 當做 List 并獲取 index 的 value 當做 Dictionary
public Dictionary<string, JsonValue> AsArrayGetObject(int index);
// JsonValue 當做 List 并獲取 index 的 value 當做 List
public List<JsonValue> AsArrayGetArray(int index);
// JsonValue 當做 List 并獲取 index 的 value 當做 string
public string AsArrayGetString(int index);
// JsonValue 當做 List 并獲取 index 的 value 當做 float
public float AsArrayGetFloat(int index);
// JsonValue 當做 List 并獲取 index 的 value 當做 int
public int AsArrayGetInt(int index);
// JsonValue 當做 List 并獲取 index 的 value 當做 bool
public bool AsArrayGetBool(int index);
// JsonValue 當做 List 并獲取 index 的 value 當做 null
public bool AsArrayGetIsNull(int index);

最后

MojoUnityJson 目的就是完成簡單而單一的JSON字符串解析功能,能夠讀取JSON的數據就是最重要的功能。在網上也了解了一些開源的C#實現的JSON庫,不是功能太多太豐富,就是實現有些繁瑣了,于是就手動實現了MojoUnityJson。

總結

以上所述是小編給大家介紹的C#實現JSON解析器MojoUnityJson,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!

原文鏈接:https://www.jianshu.com/p/76a441d1ac44

延伸 · 閱讀

精彩推薦
  • C#C#通過KD樹進行距離最近點的查找

    C#通過KD樹進行距離最近點的查找

    這篇文章主要為大家詳細介紹了C#通過KD樹進行距離最近點的查找,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    帆帆帆6112022-01-22
  • C#WPF 自定義雷達圖開發實例教程

    WPF 自定義雷達圖開發實例教程

    這篇文章主要介紹了WPF 自定義雷達圖開發實例教程,本文介紹的非常詳細,具有參考借鑒價值,需要的朋友可以參考下...

    WinterFish13112021-12-06
  • C#C# 實現對PPT文檔加密、解密及重置密碼的操作方法

    C# 實現對PPT文檔加密、解密及重置密碼的操作方法

    這篇文章主要介紹了C# 實現對PPT文檔加密、解密及重置密碼的操作方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下...

    E-iceblue5012022-02-12
  • C#Unity3D實現虛擬按鈕控制人物移動效果

    Unity3D實現虛擬按鈕控制人物移動效果

    這篇文章主要為大家詳細介紹了Unity3D實現虛擬按鈕控制人物移動效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一...

    shenqingyu060520232410972022-03-11
  • C#深入解析C#中的交錯數組與隱式類型的數組

    深入解析C#中的交錯數組與隱式類型的數組

    這篇文章主要介紹了深入解析C#中的交錯數組與隱式類型的數組,隱式類型的數組通常與匿名類型以及對象初始值設定項和集合初始值設定項一起使用,需要的...

    C#教程網6172021-11-09
  • C#C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題實例

    C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題實例

    這篇文章主要介紹了C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題,簡單描述了訪問者模式的定義并結合具體實例形式分析了C#使用訪問者模式解決長...

    GhostRider9502022-01-21
  • C#C#實現XML文件讀取

    C#實現XML文件讀取

    這篇文章主要為大家詳細介紹了C#實現XML文件讀取的相關代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    Just_for_Myself6702022-02-22
  • C#C#裁剪,縮放,清晰度,水印處理操作示例

    C#裁剪,縮放,清晰度,水印處理操作示例

    這篇文章主要為大家詳細介紹了C#裁剪,縮放,清晰度,水印處理操作示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    吳 劍8332021-12-08
主站蜘蛛池模板: 亚洲精品一区二区三区免 | 久久久看 | 午夜视频在线免费观看 | a级高清免费毛片av在线 | 视频一区二区中文字幕 | 久久久久久久久久久av | 久久久久久久不卡 | 搜一级毛片| 一区二区三区欧美日韩 | 亚洲成人精品国产 | 原来神马影院手机版免费 | 91精品久久香蕉国产线看观看 | 亚洲视频综合 | 性欧美极品xxxx欧美一区二区 | 日日综合| 男男啪羞羞视频网站 | 特级毛片免费 | 一级一级一级一级毛片 | 国产精品免费麻豆入口 | 欧美a视频 | 中文字幕国产欧美 | 日韩在线毛片 | 欧美极品欧美精品欧美视频 | 成人午夜影院 | 精品久久久久久综合日本 | 美国黄色毛片女人性生活片 | 91专区在线观看 | 天天夜干 | 久久久噜噜噜久久熟有声小说 | ⅴideo裸体秀hd | 久久久青| 国产一国产一级毛片视频 | 激情久久免费视频 | 激情视频免费看 | 欧美亚洲国产日韩 | 泰剧19禁啪啪无遮挡 | 国产精品久久久久久久久久东京 | 一区二区久久精品66国产精品 | 国产在线观看91一区二区三区 | 青草久久久久 | 在线免费91 |