來源:https://gyl-coder.top/JSONParser/
JSON
JSON(JavaScript Object Notation, JS 對象簡譜) 是一種輕量級的數(shù)據(jù)交換格式。易于人閱讀和編寫癞谒。同時也易于機(jī)器解析和生成底燎。采用完全獨立于語言的文本格式,但是也使用了類似于C語言家族的習(xí)慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)弹砚。這些特性使JSON成為理想的數(shù)據(jù)交換語言双仍。
JSON與JS的區(qū)別以及和XML的區(qū)別具體請參考百度百科:
https://baike.baidu.com/item/JSON/2462549?fr=aladdin
JSON有兩種結(jié)構(gòu):
第一種:對象
“名稱/值”對的集合不同的語言中,它被理解為對象(object)桌吃,紀(jì)錄(record)朱沃,結(jié)構(gòu)(struct),字典(dictionary)茅诱,哈希表(hash table)逗物,有鍵列表(keyed list),或者關(guān)聯(lián)數(shù)組 (associative array)瑟俭。
對象是一個無序的“‘名稱/值’對”集合翎卓。一個對象以“{”(左括號)開始,“}”(右括號)結(jié)束尔当。每個“名稱”后跟一個“:”(冒號)莲祸;“‘名稱/值’ 對”之間使用“,”(逗號)分隔蹂安。
{"姓名": "張三", "年齡": "18"}
第二種:數(shù)組
值的有序列表(An ordered list of values)。在大部分語言中锐帜,它被理解為數(shù)組(array)田盈。
數(shù)組是值(value)的有序集合。一個數(shù)組以“[”(左中括號)開始缴阎,“]”(右中括號)結(jié)束允瞧。值之間使用“,”(逗號)分隔。
值(value)可以是雙引號括起來的字符串(string)蛮拔、數(shù)值(number)述暂、true、false建炫、 null畦韭、對象(object)或者數(shù)組(array)。這些結(jié)構(gòu)可以嵌套肛跌。
通過上面的了解可以看出艺配,JSON存在以下幾種數(shù)據(jù)類型(以Java做類比):
解析JSON
JSON解析器的基本原理
輸入一串JSON字符串,輸出一個JSON對象衍慎。
步驟
JSON解析的過程主要分以下兩步:
第一步:對于輸入的一串JSON字符串我們需要將其解析成一組token流转唉。
例如 JSON字符串{“姓名”: “張三”, “年齡”: “18”} 我們需要將它解析成
{、 姓名稳捆、 :赠法、 張三、 ,乔夯、 年齡砖织、 :、 18末荐、 }
這樣一組token流
第二步:根據(jù)得到的token流將其解析成對應(yīng)的JSON對象(JSONObject)或者JSON數(shù)組(JSONArray)
下面我們來詳細(xì)分析下這兩個步驟:
獲取token流
根據(jù)JSON格式的定義镶苞,token可以分為以下幾種類型
根據(jù)以上的JSON類型,我們可以將其封裝成enum類型的TokenType
在TokenType中我們?yōu)槊恳环N類型都賦一個數(shù)字鞠评,目的是在Parser做一些優(yōu)化操作(通過位運算來判斷是否是期望出現(xiàn)的類型)
在進(jìn)行第一步之前JSON串對計算機(jī)來說只是一串沒有意義的字符而已。第一步的作用就是把這些無意義的字符串變成一個一個的token壕鹉,上面我們已經(jīng)為每一種token定義了相應(yīng)的類型和值剃幌。所以計算機(jī)能夠區(qū)分不同的token,并能以token為單位解讀JSON數(shù)據(jù)晾浴。
下面我們封裝一個token類來存儲每一個token對應(yīng)的值
在解析的過程中我們通過字符流來不斷的讀取字符负乡,并且需要經(jīng)常根據(jù)相應(yīng)的字符來判斷狀態(tài)的跳轉(zhuǎn)。所以我們需要自己封裝一個ReaderChar類脊凰,以便我們更好的操作字符流抖棘。
另外我們還需要一個TokenList來存儲解析出來的token流
JSON解析比其他文本解析要簡單的地方在于茂腥,我們只需要根據(jù)下一個字符就可知道接下來它所期望讀取的到的內(nèi)容是什么樣的。如果滿足期望了切省,則返回 Token最岗,否則返回錯誤。
為了方便程序出錯時更好的debug朝捆,程序中自定義了兩個exception類來處理錯誤信息般渡。(具體實現(xiàn)參考exception包)
下面就是第一步中的重頭戲(核心代碼):
在start方法中,我們將每個處理方法都封裝成了單獨的函數(shù)芙盘。主要思想就是通過一個死循環(huán)不停的讀取字符驯用,然后再根據(jù)字符的期待值,執(zhí)行不同的處理函數(shù)儒老。
下面我們詳解分析幾個處理函數(shù):
該方法也是通過一個死循環(huán)來讀取字符蝴乔,首先判斷的是JSON中的轉(zhuǎn)義字符。
JSON中允許出現(xiàn)的有以下幾種
"
\
\u four-hex-digits
/
具體的處理方法封裝在了isEscape()方法中驮樊,處理Unicode 編碼時要特別注意一下u的后面會出現(xiàn)四位十六進(jìn)制數(shù)薇正。當(dāng)讀取到一個雙引號或者讀取到了非法字符(’ ’或’、’ ’)循環(huán)退出巩剖。
判斷數(shù)字的時候也要特別小心铝穷,注意負(fù)數(shù),frac佳魔,exp等等情況曙聂。
通過上面的解析,我們可以得到一組token鞠鲜,接下來我們需要以這組token作為輸入宁脊,解析出相應(yīng)的JSON對象
解析出JSON對象
解析之前我們需要定義出JSON對象(JSONObject)和JSON數(shù)組(JSONArray)的實體類。
之后我們就可以寫解析類了贤姆,由于代碼較長榆苞,這里就不展示了。有興趣的可以去GitHub上下載。實現(xiàn)邏輯比較簡單衡楞,也易于理解歇僧。
解析類中的parse方法首先根據(jù)第一個token的類型選擇調(diào)用parseJsonObject()或者parseJsonArray(),進(jìn)而返回JSON對象或者JSON數(shù)組赊琳。上面的解析方法中利用位運算來判斷字符的期待值既提高了程序的執(zhí)行效率也有助于提高代碼的ke’du’xi
完成之后我們可以寫一個測試類來驗證下我們的解析器的運行情況。我們可以自己定義一組JSON串也可以通過HttpUtil工具類從網(wǎng)上獲取砰碴。最后通過FormatUtil類來規(guī)范我們輸出躏筏。
具體效果如下圖所示:
擴(kuò)展閱讀