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可以很容易的嵌入到其它项目里使用。 本文主要介绍一下,超级简单又高效,并且看一眼就完全明白的解析算法,几乎可以原封不动的复制粘贴成其它语言版本的实现。 保存上下文信息 使用一个简单的结构体,用来在解析的过程中,传递一些上下文数据。 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的值抽象成以下几个类型: public enum JsonType { Object,Array,String,Number,Bool,Null,} 整体解析步骤 // 解析 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也就解析完成了。 解析空白字符 解析过程中,会有很多为了格式化存在的空白字符,需要剔除这些,才能获得有信息的字符,这是一个重复的过程,需要一个函数统一处理。 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 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)); }
解析JsonObject 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] == ':',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] == '}',char '{1}' should be '{2}' ",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 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 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,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就可以了。 /// <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 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对象包含了所有的数值。 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,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对编程小技巧网站的支持! 您可能感兴趣的文章:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |