Golang json parser without struct === ###### tags: `技術分享` `Golang` <br><br> ## 前言-原本標準庫的json package golang 標準庫的 "encoding/json" 不只速度被屢屢被complain,外加使用上有些不便之處,所以青出於愛,更勝於愛,github上不斷有高手推陳出新推出更好的json套件。 <br><br> ## 速度以外,困擾點 json unmarshal的部分應該是最令人困擾 ``` golang // 型態 type GetUsers struct { Ret []struct { ID int `json:"id"` Parent []int `json:"parent"` Country string `json:"country"` Username string `json:"username"` } `json:"ret"` } // 變數 var getUsers GetUsers v := `{"ret":[{"id":1,"parent":[1,2,3],"country":"tokyo","username":"jax"},{"id":"1","parent":[1,2,3],"country":"kyoto","username":"nanny"}]}` // decode unmarshalErr := json.Unmarshal([]byte(v), &getUsers) if unmarshalErr != nil { log.Println(unmarshalErr.Error()) } log.Println(getUsers) ``` 1. 沒事先定義出struct,無法unmarshal 1. 如果收到的資料,型態被改變了,例如預期ID會是int的型態,突然有天拿到的資料發現是string ......也是很苦惱。 #### 上面的code執行結果如下 ``` bash $ json: cannot unmarshal string into Go struct field .id of type int $ {[{1 [1 2 3] tokyo jax} {0 [1 2 3] kyoto nanny}]} ``` 所以要嘛額外盯著error,一但有error這坨資料就不算,要嘛盯著重要欄位,重要欄位的value是預設值,才拋棄這坨資料,不管怎麼都麻煩。 <br><br> ## 人懶,就有救星出現 #### 原本被推薦這個,但下面那個更簡潔 ``` bash github.com/buger/jsonparser ``` #### 簡潔好用的json package,但某些東西還是上面好 ``` bash github.com/tidwall/gjson ``` <br><br> ## 救星為什麼是救星 * 先寫struct有個壞處,不管是事先制定struct的格式,或者struct寫下之後,如果struct長得太複雜,例如包個兩層map,三層struct,還參雜著slice,閱讀的人會非常痛苦,甚至難以理解。 * 想想jquery出來的時候會這麼受歡迎。 * json 的層次關係好理解,但struct不好懂,如果能用json的上下關係去抓取關鍵的key呢? ``` json { "name": {"first": "Tom", "last": "Anderson"}, "age":37, "children": ["Sara","Alex","Jack"], "fav.movie": "Deer Hunter", "friends": [ {"first": "Dale", "last": "Murphy", "age": 44}, {"first": "Roger", "last": "Craig", "age": 68}, {"first": "Jane", "last": "Murphy", "age": 47} ] } ``` 這樣就可以拿到last name ``` go // import "github.com/tidwall/gjson" gjson.Get(json, "name").Get("last").String() ``` <br> ## 但如果這麼厲害,為什麼不納入標準庫使用? * 靜態語言就是有靜態語言的難處,不先宣告type的東西,事後轉換型態就是會有一定的困難,或者已經無法用type assert 去指定它應該是什麼型態。 * 所以若非int,string,float,bool等基本型態,複雜的slice或map就無法處理。 <br> ## 那想要簡單,又想要使用複雜的型態該怎麼辦? * 有個idea很重要,假設進來的資料是個貨真價實的json格式。 * json格式,不外乎就分兩種,array和object。(這兩個絕對絕對不同喔![]包起來和{},完全是不同格式) * 以json的角度來考慮,就是array和object。 * 所以兩個package都有提供isArray和isObject的判斷method。 #### 觀念繼續延伸 1. 用package的方式,想要的資料在json格式的哪一層,要取得很方便。 1. 取到的東西,複雜型態,要嘛可以當作一個單元的slice,或者map。 1. 因為json object的條件是需要有key和value,在golang的對應裡面就是map。 1. 所以gjson在此提供的Array()和Map()兩個method,就可以達到我們要的處理,全部被混在一起的interface{}和能夠被切開的interface{},使用性是不同概念。 ###### 如下,在golang是想要以[]string來使用 ###### 這整個是interface{},無法直接用(.[]string)做型態指定 ``` json ["1,1,4,5,6","2,2,4,3,2","1,2,1,3,4","2,2,4,2,5","5,2,4,3,5"] ``` ###### 透過Array(),則可以拆成5份,每份在利用.String()轉回string,重新組裝成[]string就達到目的了。 ``` code 1,1,4,5,6 2,2,4,3,2 1,2,1,3,4 2,2,4,2,5 5,2,4,3,5 ```