encoding/json
是官方提供的標準json, 實現RFC 7159中定義的JSON編碼和解碼。使用的時候須要預約義struct
,原理是經過reflection
和interface
來完成工做, 性能低。html
經常使用的接口:git
func Marshal(v interface{}) ([]byte, error)
生成JSONfunc Unmarshal(data []byte, v interface{}) error
解析JSON到struct
示例1 生成JSON:github
type ColorGroup struct { ID int Name string Colors []string } group := ColorGroup{ ID: 1, Name: "Reds", Colors: []string{"Crimson", "Red", "Ruby", "Maroon"}, } b, err := json.Marshal(group) if err != nil { fmt.Println("error:", err) } os.Stdout.Write(b)
Output:json
{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
示例2 解析JSON:數組
var jsonBlob = []byte(`[ {"Name": "Platypus", "Order": "Monotremata"}, {"Name": "Quoll", "Order": "Dasyuromorphia"} ]`) type Animal struct { Name string Order string } var animals []Animal err := json.Unmarshal(jsonBlob, &animals) if err != nil { fmt.Println("error:", err) } fmt.Printf("%+v", animals)
Output:bash
[{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
easyjson, ffjson 並無使用反射方式實現,而是在Go中爲結構體生成靜態MarshalJSON
和UnmarshalJSON
函數。生成的函數減小了對運行時反射的依賴,因此一般快2到3倍。但相比標準JSON包,使用起來略爲繁瑣。app
使用步驟:elasticsearch
一、定義結構體,每一個結構體註釋裏標註 //easyjson:json
或者 //ffjson: skip
;
二、使用 easyjson
或者ffjson
命令將指定目錄的go結構體文件生成帶有Marshal
、UnMarshal
方法的新文件;
三、代碼裏若是須要進行生成JSON或者解析JSON,調用生成文件的 Marshal
、UnMarshal
方法便可。函數
下面是使用示例。性能
GitHub:https://github.com/mailru/easyjson
一、先安裝:
go get -u github.com/mailru/easyjson/
二、定義結構體:
記得在須要使用easyjson
的結構體上加上//easyjson:json
。 以下:
//easyjson:json type School struct { Name string `json:"name"` Addr string `json:"addr"` } //easyjson:json type Student struct { Id int `json:"id"` Name string `json:"s_name"` School School `json:"s_chool"` Birthday time.Time `json:"birthday"` }
三、在結構體包下執行
easyjson -all student.go
此時在該目錄下出現一個新的文件:easyjson_student.go,該文件給結構體增長了MarshalJSON
、UnmarshalJSON
等方法。
四、使用
package main import ( "studygo/easyjson" "time" "fmt" ) func main(){ s:=easyjson.Student{ Id: 11, Name:"qq", School:easyjson.School{ Name:"CUMT", Addr:"xz", }, Birthday:time.Now(), } bt,err:=s.MarshalJSON() fmt.Println(string(bt),err) json:=`{"id":11,"s_name":"qq","s_chool":{"name":"CUMT","addr":"xz"},"birthday":"2017-08-04T20:58:07.9894603+08:00"}` ss:=easyjson.Student{} ss.UnmarshalJSON([]byte(json)) fmt.Println(ss) }
運行結果:
{"id":11,"s_name":"qq","s_chool":{"name":"CUMT","addr":"xz"},"birthday":"2017-08-04T20:58:07.9894603+08:00"} <nil> {121 {CwwwwwwwUMT xzwwwww} 2017-08-04 20:52:03.4066002 +0800 CST}
GitHub:https://github.com/pquerna/ffjson
本小節就不給示例了,你們直接看github上說明。用法與easyjson相似。
須要注意的是,ffjson也提供了ffjson.Marshal
和ffjson.Unmarshal
方法,若是沒有使用ffjson給對應結構體生成靜態的方法,則會調用標準庫encoding/json
進行編碼解碼:
func Marshal(v interface{}) ([]byte, error) { //調用結構體的靜態方法 f, ok := v.(marshalerFaster) if ok { buf := fflib.Buffer{} err := f.MarshalJSONBuf(&buf) b := buf.Bytes() if err != nil { if len(b) > 0 { Pool(b) } return nil, err } return b, nil } //調用encoding/json j, ok := v.(json.Marshaler) if ok { return j.MarshalJSON() } return json.Marshal(v) }
這幾個包都是在encoding/json
的基礎上進行開發的,爲了是更方便的操做JSON:它不須要建立struct,而是動態按字段取內容。有時候咱們僅僅想取JSON裏的某個字段,用這個很是有用。
下面是go-simplejson
示例。
Github: https://github.com/bitly/go-simplejson
package main import ( "fmt" "github.com/bitly/go-simplejson" ) func main() { data := []byte(`{ "hits":{ "total":2, "max_score":4.631368, "hits":[ { "_source":{ "account_number":298, "balance":34334, "firstname":"Bullock", "lastname":"Marsh" } } ] } }`) js, _ := simplejson.NewJson(data) //get total total, _ := js.Get("hits").Get("total").Int64() fmt.Println(total) account_number, _ := js.Get("hits").Get("hits").GetIndex(0).Get("_source").Get("account_number").Int64() fmt.Println(account_number) //get _source list hitsjson, _ := js.Get("hits").Get("hits").MarshalJSON() fmt.Printf("%s", hitsjson) }
輸出:
2 298 [{"_id":"298","_index":"bank","_score":4.631368,"_source":{"account_number":298,"balance":34334,"firstname":"Bullock","lastname":"Marsh"},"_type":"account"}]
go-simplejson 沒有提供相似Each方法,沒法對數組類型的進行遍歷。可是咱們能夠將數組取到後調用MarshalJSON
生成JSON,使用標準的encoding/json
進行解析。
Github: https://github.com/Jeffail/gabs
package main import ( "fmt" "github.com/Jeffail/gabs/v2" ) func main() { data := []byte(`{}`) //注:爲節省篇幅,data結構參考go-simplejson js, _ := gabs.ParseJSON(data) //get total var total float64 //使用斷言,不然類型錯誤會報錯 if val, ok := js.Path("hits.total").Data().(float64); ok { total = val } total2 := js.Search("hits", "total").Data().(float64) total3 := js.S("hits", "total").Data().(float64) // S is shorthand for Search gObj, _ := js.JSONPointer("/hits/total") total4 := gObj.Data().(float64) fmt.Println(total, total2, total3, total4) exist := js.Exists("hits", "total") fmt.Println(exist) account_number := js.Path("hits.hits.0._source.account_number").Data().(float64) fmt.Println(account_number) //Iterating arrays for _, v := range js.S("hits", "hits").Children() { lastname := v.S("_source", "lastname").Data().(string) fmt.Printf("%v\n", lastname) } }
輸出:
2 2 2 2 true 298 Marsh
除此以外,gabs 還支持從新動態生成JSON、合併JSON等操做。可是解析須要使用斷言這一點不是很方便。
Github: https://github.com/antonholmquist/jason
示例:
package main import ( "fmt" "github.com/antonholmquist/jason" ) func main() { data := []byte(`{}`) //注:爲節省篇幅,data結構參考go-simplejson js, _ := jason.NewObjectFromBytes(data) //get total total, _ := js.GetInt64("hits", "total") fmt.Println(total) //get _source list hitsjson, _ := js.GetObjectArray("hits", "hits") for _, v := range hitsjson { lastname, _ := v.GetString("_source", "lastname") fmt.Printf("%v\n", lastname) } }
輸出:
2 Marsh
提供了遍歷數組的方法,可是沒有提供按索引取某個數組的方法。
jsonparser 功能與go-simplejson
相似,可是因爲底層不是基於encoding/json
開發的,官方宣稱它比encoding/json
快10倍。
GitHub: https://github.com/buger/jsonparser
下面是個解析ES的示例:
package main import ( "encoding/json" "fmt" "github.com/buger/jsonparser" ) type UserInfo struct { AccountNumber int64 `json:"account_number"` Balance int64 `json:"balance"` Firstname string `json:"firstname"` Lastname string `json:"lastname"` } func main() { data := []byte(`{}`) //注:爲節省篇幅,data結構參考go-simplejson //get total total, _ := jsonparser.GetInt(data, "hits", "total") fmt.Println(total) //get _source list var list []UserInfo hitsjson, _, _, _ := jsonparser.Get(data, "hits", "hits") type hitsMap struct { Source UserInfo `json:"_source,omitempty"` } var hitsMaps []hitsMap json.Unmarshal(hitsjson, &hitsMaps) for _, info := range hitsMaps { list = append(list, info.Source) } fmt.Printf("%+v\n", list) //get each _source jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) { _source, _, _, _ := jsonparser.Get(value, "_source") fmt.Println(string(_source)) }, "hits", "hits") }
輸出:
2 [{AccountNumber:298 Balance:34334 Firstname:Bullock Lastname:Marsh}] { "account_number": 298, "balance": 34334, "firstname": "Bullock", "lastname": "Marsh" }
你們能夠看一下 elastic/go-elasticsearch 給出的示例裏是怎麼解析JSON的:
// Print the response status, number of results, and request duration. log.Printf( "[%s] %d hits; took: %dms", res.Status(), int(r["hits"].(map[string]interface{})["total"].(map[string]interface{})["value"].(float64)), int(r["took"].(float64)), ) // Print the ID and document source for each hit. for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) { log.Printf(" * ID=%s, %s", hit.(map[string]interface{})["_id"], hit.(map[string]interface{})["_source"]) }
對,就是使用的斷言,這個會讓人很崩潰,萬一值不存在或者類型不對,還會直接扔個ERROR...
大部分狀況下你們直接使用 encoding/json
就好了,若是對性能要求很高的話,可使用 easyjson, ffjson
。遇到解析ES搜索返回的複雜的JSON或者僅須要解析個別字段, go-simplejson
或者jsonparser
就很方便了。
一、json - GoDoc
https://godoc.org/encoding/json#example-Unmarshal
二、Golang的json包一覽 - 知乎
https://zhuanlan.zhihu.com/p/24451749
三、bitly/go-simplejson: a Go package to interact with arbitrary JSON
https://github.com/bitly/go-simplejson
四、buger/jsonparser: Alternative JSON parser for Go that does not require schema (so far fastest)
https://github.com/buger/jsonparser
五、Golang高性能json包:easyjson - 夢朝思夕的我的空間 - OSCHINA
https://my.oschina.net/qiangmzsx/blog/1503018
六、pquerna/ffjson: faster JSON serialization for Go
https://github.com/pquerna/ffjson
七、Jeffail/gabs: For parsing, creating and editing unknown or dynamic JSON in Go
https://github.com/Jeffail/gabs