




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領
文檔簡介
第go語言用八百行代碼實現(xiàn)一個JSON解析器目錄前言實現(xiàn)原理詞法分析提前檢查生成JSONObject樹總結(jié)
前言
之前在寫gscript時我就在想有沒有利用編譯原理實現(xiàn)一個更實際工具?畢竟真寫一個語言的難度不低,并且也很難真的應用起來。
一次無意間看到有人提起JSON解析器,這類工具充斥著我們的日常開發(fā),運用非常廣泛。
以前我也有思考過它是如何實現(xiàn)的,過程中一旦和編譯原理扯上關系就不由自主的勸退了;但經(jīng)過這段時間的實踐我發(fā)現(xiàn)實現(xiàn)一個JSON解析器似乎也不困難,只是運用到了編譯原理前端的部分知識就完全足夠了。
得益于JSON的輕量級,同時語法也很簡單,所以核心代碼大概只用了800行便實現(xiàn)了一個語法完善的JSON解析器。
首先還是來看看效果:
import"/crossoverJie/xjson"
funcTestJson(t*testing.T){
str:=`{
"glossary":{
"title":"exampleglossary",
"age":1,
"long":99.99,
"GlossDiv":{
"title":"S",
"GlossList":{
"GlossEntry":{
"ID":"SGML",
"SortAs":"SGML",
"GlossTerm":"StandardGeneralizedMarkupLanguage",
"Acronym":"SGML",
"Abbrev":"ISO8879:1986",
"GlossDef":{
"para":"Ameta-markuplanguage,usedtocreatemarkuplanguagessuchasDocBook.",
"GlossSeeAlso":["GML","XML",true,null]
"GlossSee":"markup"
decode,err:=xjson.Decode(str)
assert.Nil(t,err)
fmt.Println(decode)
v:=decode.(map[string]interface{})
glossary:=v["glossary"].(map[string]interface{})
assert.Equal(t,glossary["title"],"exampleglossary")
assert.Equal(t,glossary["age"],1)
assert.Equal(t,glossary["long"],99.99)
glossDiv:=glossary["GlossDiv"].(map[string]interface{})
assert.Equal(t,glossDiv["title"],"S")
glossList:=glossDiv["GlossList"].(map[string]interface{})
glossEntry:=glossList["GlossEntry"].(map[string]interface{})
assert.Equal(t,glossEntry["ID"],"SGML")
assert.Equal(t,glossEntry["SortAs"],"SGML")
assert.Equal(t,glossEntry["GlossTerm"],"StandardGeneralizedMarkupLanguage")
assert.Equal(t,glossEntry["Acronym"],"SGML")
assert.Equal(t,glossEntry["Abbrev"],"ISO8879:1986")
glossDef:=glossEntry["GlossDef"].(map[string]interface{})
assert.Equal(t,glossDef["para"],"Ameta-markuplanguage,usedtocreatemarkuplanguagessuchasDocBook.")
glossSeeAlso:=glossDef["GlossSeeAlso"].(*[]interface{})
assert.Equal(t,(*glossSeeAlso)[0],"GML")
assert.Equal(t,(*glossSeeAlso)[1],"XML")
assert.Equal(t,(*glossSeeAlso)[2],true)
assert.Equal(t,(*glossSeeAlso)[3],"")
assert.Equal(t,glossEntry["GlossSee"],"markup")
從這個用例中可以看到支持字符串、布爾值、浮點、整形、數(shù)組以及各種嵌套關系。
實現(xiàn)原理
這里簡要說明一下實現(xiàn)原理,本質(zhì)上就是兩步:
詞法解析:根據(jù)原始輸入的JSON字符串解析出token,也就是類似于{objage1[]這樣的標識符,只是要給這類標識符分類。根據(jù)生成的一組token集合,以流的方式進行讀取,最終可以生成圖中的樹狀結(jié)構(gòu),也就是一個JSONObject。
下面來重點看看這兩個步驟具體做了哪些事情。
詞法分析
BeginObject{
String"name"
SepColon:
String"cj"
SepComma,
String"object"
SepColon:
BeginObject{
String"age"
SepColon:
Number10
SepComma,
String"sex"
SepColon:
String"girl"
EndObject}
SepComma,
String"list"
SepColon:
BeginArray[
其實詞法解析就是構(gòu)建一個有限自動機的過程(DFA),目的是可以生成這樣的集合(token),只是我們需要將這些token進行分類以便后續(xù)做語法分析的時候進行處理。
比如{這樣的左花括號就是一個BeginObject代表一個對象聲明的開始,而}則是EndObject代表一個對象的結(jié)束。
其中name這樣的就被認為是String字符串,以此類推[代表BeginArray
這里我一共定義以下幾種token類型:
typeTokenstring
const(
InitToken="Init"
BeginObject="BeginObject"
EndObject="EndObject"
BeginArray="BeginArray"
EndArray="EndArray"
Null="Null"
Null1="Null1"
Null2="Null2"
Null3="Null3"
Number="Number"
Float="Float"
BeginString="BeginString"
EndString="EndString"
String="String"
True="True"
True1="True1"
True2="True2"
True3="True3"
False="False"
False1="False1"
False2="False2"
False3="False3"
False4="False4"
//SepColon:
SepColon="SepColon"
//SepComma,
SepComma="SepComma"
EndJson="EndJson"
其中可以看到true/false/null會有多個類型,這點先忽略,后續(xù)會解釋。
以這段JSON為例:{age:1},它的狀態(tài)扭轉(zhuǎn)如下圖:
總的來說就是依次遍歷字符串,然后更新一個全局狀態(tài),根據(jù)該狀態(tài)的值進行不同的操作。
部分代碼如下:
感興趣的朋友可以跑跑單例debug一下就很容易理解:
以這段JSON為例:
funcTestInitStatus(t*testing.T){
str:=`{"name":"cj","age":10}`
tokenize,err:=Tokenize(str)
assert.Nil(t,err)
for_,tokenType:=rangetokenize{
fmt.Printf("%s%s\n",tokenType.T,tokenType.Value)
最終生成的token集合如下:
BeginObject{
String"name"
SepColon:
String"cj"
SepComma,
String"age"
SepColon:
Number10
EndObject}
提前檢查
由于JSON的語法簡單,一些規(guī)則甚至在詞法規(guī)則中就能校驗。
舉個例子:JSON中允許null值,當我們字符串中存在nunul這類不匹配null的值時,就可以提前拋出異常。
比如當檢測到第一個字符串為n時,那后續(xù)的必須為u-l-l不然就拋出異常。
浮點數(shù)同理,當一個數(shù)值中存在多個.點時,依然需要拋出異常。
這也是前文提到true/false/null這些類型需要有多個中間狀態(tài)的原因。
生成JSONObject樹
在討論生成JSONObject樹之前我們先來看這么一個問題,給定一個括號集合,判斷是否合法。
[()]這樣是合法的。[())而這樣是不合法的。
如何實現(xiàn)呢?其實也很簡單,只需要利用棧就能完成,如下圖所示:
利用棧的特性,依次遍歷數(shù)據(jù),遇到是左邊的符號就入棧,當遇到是右符號時就與棧頂數(shù)據(jù)匹配,能匹配上就出棧。
當匹配不上時則說明格式錯誤,數(shù)據(jù)遍歷完畢后如果棧為空時說明數(shù)據(jù)合法。
其實仔細觀察JSON的語法也是類似的:
{
"name":"cj",
"object":{
"age":10,
"sex":"girl"
"list":[
"1":"a"
"2":"b"
BeginObject:{與EndObject:}一定是成對出現(xiàn)的,中間如論怎么嵌套也是成對的。而對于age:10這樣的數(shù)據(jù),:冒號后也得有數(shù)據(jù)進行匹配,不然就是非法格式。
所以基于剛才的括號匹配原理,我們也能用類似的方法來解析token集合。
我們也需要創(chuàng)建一個棧,當遇到BeginObject時就入棧一個Map,當遇到一個String鍵時也將該值入棧。
當遇到value時,就將出棧一個key,同時將數(shù)據(jù)寫入當前棧頂?shù)膍ap中。
當然在遍歷token的過程中也需要一個全局狀態(tài),所以這里也是一個有限狀態(tài)機。
舉個例子:當我們遍歷到Token類型為String,值為name時,預期下一個token應當是:冒號;
所以我們得將當前的status記錄為StatusColon,一旦后續(xù)解析到token為SepColon時,就需要判斷當前的status是否為StatusColon,如果不是則說明語法錯誤,就可以拋出異常。
同時值得注意的是這里的status其實是一個集合,因為下一個狀態(tài)可能是多種情況。
{e:[1,[2,3],{d:{f:f}}]}比如當我們解析到一個SepColon冒號時,后續(xù)的狀態(tài)可能是value或BeginObject{或BeginArray[
因此這里就得把這三種情況都考慮到,其他的以此類推。
具體解析過程可以參考源碼:
/crossoverJie/xjson/blob/main/parse.go
雖然是借助一個棧結(jié)構(gòu)就能將JSON解析完畢,不知道大家發(fā)現(xiàn)一個問題沒有:這樣非常容易遺漏規(guī)則,比如剛才提到的一個冒號后面就有三種情況,而一個BeginArray后甚至有四種情況
StatusArrayValue,StatusBeginArray,StatusBeginObject,StatusEndArray
這樣的代碼讀起來也不是很直觀,同時容易遺漏語法,只能出現(xiàn)問題再進行修復。
既然提到了問題那自然也有相應的解決方案,其實就是語法分析中常見的遞歸下降算法。
我們只需要根據(jù)JSON的文法定義,遞歸的寫出算法即可,這樣代碼閱讀起來非常清晰,同時也不會遺漏規(guī)則。
完整的JSON語法查看這里:
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 商業(yè)合同協(xié)議書
- 車輛貼膜合同協(xié)議書模板
- 貨物采購簡易合同協(xié)議書
- 扶梯拆除合同協(xié)議書
- 結(jié)婚協(xié)議合同協(xié)議書
- 學生禁毒教育心得體會模版
- 輔警刑法筆試題及答案
- 豬場出租合同協(xié)議書
- 完成合同協(xié)議書
- 合同約定協(xié)議書打印
- 延長石油招聘筆試試題
- DB-T 29-22-2024 天津市住宅設計標準
- 古代詩人名人韓愈人物介紹課件
- 中華護理學會成人腸內(nèi)營養(yǎng)支持護理團標解讀
- 《1.4莖和葉》說課稿、教案、教學設計和同步練習
- 部編版《道德與法治》六年級下冊第1課《學會尊重》精美課件
- 企業(yè)VI設計報價清單
- 國家開放大學《現(xiàn)代教育原理》形考任務1-5參考答案
- 政治審查表(模板)
- 數(shù)字貿(mào)易學 課件 第20、21章 數(shù)字絲綢之路與數(shù)字基礎設施、數(shù)字自由貿(mào)易與數(shù)字貿(mào)易壁壘
- 地理畢業(yè)生實習報告5000字范本2篇
評論
0/150
提交評論