這篇文章主要介紹了C#如何實(shí)現(xiàn)JSON解析器MojoUnityJson功能,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
創(chuàng)新互聯(lián)主營(yíng)銅仁網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,重慶APP開(kāi)發(fā),銅仁h5微信小程序定制開(kāi)發(fā)搭建,銅仁網(wǎng)站營(yíng)銷推廣歡迎銅仁等地區(qū)企業(yè)咨詢MojoUnityJson 是使用C#實(shí)現(xiàn)的JSON解析器 ,算法思路來(lái)自于游戲引擎Mojoc的C語(yǔ)言實(shí)現(xiàn) Json.h 。借助C#的類庫(kù),可以比C的實(shí)現(xiàn)更加的簡(jiǎn)單和全面,尤其是處理Unicode Code(\u開(kāi)頭)字符的解析,C#的StringBuilder本身就支持了UnicodeCodePoint。
MojoUnityJson使用遞歸下降的解析模式,核心解析代碼只有450行(去掉空行可能只有300多行),支持標(biāo)準(zhǔn)的JSON格式。算法實(shí)現(xiàn)力求簡(jiǎn)潔明了,用最直接最快速的方法達(dá)到目的,沒(méi)有復(fù)雜的概念和模式。除了解析JSON,還提供了一組方便直觀的API來(lái)訪問(wèn)JSON數(shù)據(jù),整體實(shí)現(xiàn)只有一個(gè)文件,僅依賴 System.Collections.Generic , System.Text , System 三個(gè)命名空間,MojoUnityJson可以很容易的嵌入到其它項(xiàng)目里使用。
本文主要介紹一下,超級(jí)簡(jiǎn)單又高效,并且看一眼就完全明白的解析算法,幾乎可以原封不動(dòng)的復(fù)制粘貼成其它語(yǔ)言版本的實(shí)現(xiàn)。
保存上下文信息
使用一個(gè)簡(jiǎn)單的結(jié)構(gòu)體,用來(lái)在解析的過(guò)程中,傳遞一些上下文數(shù)據(jù)。
private struct Data { // 需要解析的JSON字符串 public string json; // 當(dāng)前JSON字符串解析的位置索引 public int index; // 緩存一個(gè)StringBuilder,用來(lái)?yè)赋鯦SON的一段字符。 public StringBuilder sb; public Data(string json, int index) { this.json = json; this.index = index; this.sb = new StringBuilder(); } }
抽象JSON的值
我們把JSON的值抽象成以下幾個(gè)類型:
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中會(huì)根據(jù)字符判斷類型,分別調(diào)用下面幾個(gè)不同的解析函數(shù)。JsonValue就對(duì)應(yīng)一個(gè)JSON的值,它有一個(gè)JsonType代表了這個(gè)值的類型。這是一個(gè)遞歸的過(guò)程,在ParseValue,ParseObject和ParseArray過(guò)程中,會(huì)遞歸的調(diào)用ParseValue。JSON一定是始于一個(gè),Object或Array,當(dāng)這個(gè)最頂層的值解析完畢的時(shí)候,整個(gè)JSON也就解析完成了。
解析空白字符
解析過(guò)程中,會(huì)有很多為了格式化存在的空白字符,需要剔除這些,才能獲得有信息的字符,這是一個(gè)重復(fù)的過(guò)程,需要一個(gè)函數(shù)統(tǒng)一處理。
private static void SkipWhiteSpace(ref Data data) { while (true) { switch (data.json[data.index]) { case ' ' : case '\t': case '\n': case '\r': data.index++; // 每次消耗一個(gè)字符,就向后推進(jìn)JSON的索引 continue; } break; } }
解析JsonValue
private static JsonValue ParseValue(ref Data data) { // 跳過(guò)空白字符 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 '-': // 表示數(shù)值 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這個(gè)抽象的JSON值,其真實(shí)的類型在解析的過(guò)程中逐漸具體化。
在剝離掉空白字符之后,就可以很容易的通過(guò)單個(gè)字符,就判斷出其可能的數(shù)值類型,而不需要向前或向后檢索。
true,false,null 這幾個(gè)固定的類型,直接就處理掉了,而其它稍微復(fù)雜的類型需要使用函數(shù)來(lái)處理。
這里沒(méi)有使用if else,而是大量使用了case,是為了提高效率,減少判斷次數(shù)。
解析JsonObject
private static JsonValue ParseObject(ref Data data) { // Object 對(duì)應(yīng) C#的Dictionary var jsonObject = new Dictionary(JsonObjectInitCapacity); // skip '{' data.index++; do { // 跳過(guò)空白字符 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); // 跳過(guò)空白 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 // 遞歸的調(diào)用ParseValue獲得Object的value值 jsonObject.Add(key, ParseValue(ref data)); // 跳過(guò)空白 SkipWhiteSpace(ref data); if (data.json[data.index] == ',') { // Object的下一對(duì)KV data.index++ ; } else { // 跳過(guò)空白 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類型就簡(jiǎn)單的對(duì)應(yīng)C#的Dictionary,value是JsonValue類型。當(dāng)解析完成后,value的類型就是確定的了。
JsonValue是遞歸的調(diào)用ParseValue來(lái)處理的,其類型可能是JsonType枚舉的任意類型。
解析JsonArray
private static JsonValue ParseArray(ref Data data) { // JsonArray 對(duì)應(yīng) List var jsonArray = new List(JsonArrayInitCapacity); // skip '[' data.index++; do { // 跳過(guò)空白 SkipWhiteSpace(ref data); if (data.json[data.index] == ']') { // 空 "[]" break; } // add JsonArray item // 遞歸處理List每個(gè)元素 jsonArray.Add(ParseValue(ref data)); // 跳過(guò)空白 SkipWhiteSpace(ref data); if (data.json[data.index] == ',') { // 解析下一個(gè)元素 data.index++; } else { // 跳過(guò)空白 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類型就簡(jiǎn)單的對(duì)應(yīng)C#的List,element是JsonValue類型。當(dāng)解析完成后,element的類型就是確定的了。
JsonValue是遞歸的調(diào)用ParseValue來(lái)處理的,其類型可能是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 '"': // 字符串結(jié)束 // check end '"' if (data.sb.Length == 0) { // 沒(méi)有使用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; // 處理各種轉(zhuǎn)義字符 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': // 計(jì)算unicode字符的碼點(diǎn) 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); }
處理字符串麻煩的地方在于,轉(zhuǎn)義字符需要特殊處理,都這轉(zhuǎn)義字符就會(huì)直接顯示而不能展示特殊的作用。好在StringBuilder功能非常強(qiáng)大,提供處理各種情況的接口。
解析Unicode字符
在JSON中,Unicode字符是以\u開(kāi)頭跟隨4個(gè)碼點(diǎn)組成的轉(zhuǎn)義字符。碼點(diǎn)在StringBuilder的Append重載函數(shù)中是直接支持的。所以,我們只要把\u后面的4個(gè)字符,轉(zhuǎn)換成碼點(diǎn)傳遞給Append就可以了。
////// Get the unicode code point. /// private static char GetUnicodeCodePoint(char c1, char c2, char c3, char c4) { // 把\u后面的4個(gè)char轉(zhuǎn)換成碼點(diǎn),注意這里需要是char類型,才能被Append正確處理。 // 4個(gè)char轉(zhuǎn)換為int后,映射到16進(jìn)制的高位到低位,然后相加得到碼點(diǎn)。 return (char) ( UnicodeCharToInt(c1) * 0x1000 + UnicodeCharToInt(c2) * 0x100 + UnicodeCharToInt(c3) * 0x10 + UnicodeCharToInt(c4) ); } ////// Single unicode char convert to int. /// 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; // 收集數(shù)值字符 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; } // 摳出數(shù)值字符串 var strNum = data.json.Substring(start, data.index - start); float num; // 當(dāng)成float處理,當(dāng)然也可以用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對(duì)象,然后JsonValue對(duì)象包含了所有的數(shù)值。
var jsonValue = MojoUnity.Json.Parse(jsonString); JsonValue的訪問(wèn)API // JsonValue 當(dāng)做 string public string AsString(); // JsonValue 當(dāng)做 float public float AsFloat(); // JsonValue 當(dāng)做 int public float AsInt(); // JsonValue 當(dāng)做 bool public float AsBool(); // JsonValue 當(dāng)做 null public float IsNull(); // JsonValue 當(dāng)做 Dictionary public DictionaryAsObject(); // JsonValue 當(dāng)做 Dictionary 并根據(jù) key 獲取 value 當(dāng)做JsonValue public JsonValue AsObjectGet(string key); // JsonValue 當(dāng)做 Dictionary 并根據(jù) key 獲取 value 當(dāng)做 Dictionary public Dictionary AsObjectGetObject(string key); // JsonValue 當(dāng)做 Dictionary 并根據(jù) key 獲取 value 當(dāng)做 List public List AsObjectGetArray(string key); // JsonValue 當(dāng)做 Dictionary 并根據(jù) key 獲取 value 當(dāng)做 string public string AsObjectGetString(string key); // JsonValue 當(dāng)做 Dictionary 并根據(jù) key 獲取 value 當(dāng)做 float public float AsObjectGetFloat(string key); // JsonValue 當(dāng)做 Dictionary 并根據(jù) key 獲取 value 當(dāng)做 int public int AsObjectGetInt(string key); // JsonValue 當(dāng)做 Dictionary 并根據(jù) key 獲取 value 當(dāng)做 bool public bool AsObjectGetBool(string key); // JsonValue 當(dāng)做 Dictionary 并根據(jù) key 獲取 value 當(dāng)做 null public bool AsObjectGetIsNull(string key); // JsonValue 當(dāng)做 List public List AsArray(); // JsonValue 當(dāng)做 List 并獲取 index 的 value 當(dāng)做 JsonValue public JsonValue AsArrayGet(int index); // JsonValue 當(dāng)做 List 并獲取 index 的 value 當(dāng)做 Dictionary public Dictionary AsArrayGetObject(int index); // JsonValue 當(dāng)做 List 并獲取 index 的 value 當(dāng)做 List public List AsArrayGetArray(int index); // JsonValue 當(dāng)做 List 并獲取 index 的 value 當(dāng)做 string public string AsArrayGetString(int index); // JsonValue 當(dāng)做 List 并獲取 index 的 value 當(dāng)做 float public float AsArrayGetFloat(int index); // JsonValue 當(dāng)做 List 并獲取 index 的 value 當(dāng)做 int public int AsArrayGetInt(int index); // JsonValue 當(dāng)做 List 并獲取 index 的 value 當(dāng)做 bool public bool AsArrayGetBool(int index); // JsonValue 當(dāng)做 List 并獲取 index 的 value 當(dāng)做 null public bool AsArrayGetIsNull(int index);
最后
MojoUnityJson 目的就是完成簡(jiǎn)單而單一的JSON字符串解析功能,能夠讀取JSON的數(shù)據(jù)就是最重要的功能。在網(wǎng)上也了解了一些開(kāi)源的C#實(shí)現(xiàn)的JSON庫(kù),不是功能太多太豐富,就是實(shí)現(xiàn)有些繁瑣了,于是就手動(dòng)實(shí)現(xiàn)了MojoUnityJson。
C#是一個(gè)簡(jiǎn)單、通用、面向?qū)ο蟮木幊陶Z(yǔ)言,它由微軟Microsoft開(kāi)發(fā),繼承了C和C++強(qiáng)大功能,并且去掉了一些它們的復(fù)雜特性,C#綜合了VB簡(jiǎn)單的可視化操作和C++的高運(yùn)行效率,以其強(qiáng)大的操作能力、優(yōu)雅的語(yǔ)法風(fēng)格、創(chuàng)新的語(yǔ)言特性和便捷的面向組件編程從而成為.NET開(kāi)發(fā)的選語(yǔ)言,但它不適用于編寫時(shí)間急迫或性能非常高的代碼,因?yàn)镃#缺乏性能極高的應(yīng)用程序所需要的關(guān)鍵功能。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“C#如何實(shí)現(xiàn)JSON解析器MojoUnityJson功能”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián)成都做網(wǎng)站,關(guān)注創(chuàng)新互聯(lián)成都做網(wǎng)站行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。