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
```