這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
簡介
easyjson是什麼呢?根據官網介紹,easyjson是提供高效快速且易用的結構體structs<-->json轉換包。easyjson並沒有使用反射方式實現,所以效能比其他的json包該4-5倍,比golang 內建的json包快2-3倍。easyjson目標是維持產生去代碼簡單,以致於它可以輕鬆地進行最佳化或固定。
安裝
go get -u github.com/mailru/easyjson/go install github.com/mailru/easyjson/easyjsonorgo build -o easyjson github.com/mailru/easyjson/easyjson
驗證是否安裝成功。
$ easyjsonUsage of D:\Code\go\bin\easyjson.exe: -all generate marshaler/unmarshalers for all structs in a file -build_tags string build tags to add to generated file -leave_temps do not delete temporary files -lower_camel_case use lowerCamelCase names instead of CamelCase by default -no_std_marshalers don't generate MarshalJSON/UnmarshalJSON funcs -noformat do not run 'gofmt -w' on output file -omit_empty omit empty fields by default string specify the filename of the output -pkg process the whole package instead of just the given file -snake_case use snake_case names instead of CamelCase by default -stubs only generate stubs for marshaler/unmarshaler funcs
其中有幾個選項需要注意:
-lower_camel_case:將結構體欄位field首字母改為小寫。如Name=>name。 -build_tags string:將指定的string產生到產生的go檔案頭部。 -no_std_marshalers:不為結構體產生MarshalJSON/UnmarshalJSON函數。 -omit_empty:沒有賦值的field可以不產生到json,否則field為該欄位類型的預設值。-output_filename:定義產生的檔案名稱。-pkg:對包內指定有`//easyjson:json`結構體產生對應的easyjson配置。-snke_case:可以底線的field如`Name_Student`改為`name_student`。
使用
記得在需要使用easyjson的結構體上加上//easyjson:json。如下:
//easyjson:jsontype School struct {Name string`json:"name"`Addr string`json:"addr"`}//easyjson:jsontype 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
此時在該目錄下出現一個新的檔案。
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.package easyjsonimport (json "encoding/json"easyjson "github.com/mailru/easyjson"jlexer "github.com/mailru/easyjson/jlexer"jwriter "github.com/mailru/easyjson/jwriter")// suppress unused package warningvar (_ *json.RawMessage_ *jlexer.Lexer_ *jwriter.Writer_ easyjson.Marshaler)func easyjsonB83d7b77DecodeStudygoEasyjson(in *jlexer.Lexer, out *Student) {isTopLevel := in.IsStart()if in.IsNull() {if isTopLevel {in.Consumed()}in.Skip()return}in.Delim('{')for !in.IsDelim('}') {key := in.UnsafeString()in.WantColon()if in.IsNull() {in.Skip()in.WantComma()continue}switch key {case "id":out.Id = int(in.Int())case "s_name":out.Name = string(in.String())case "s_chool":easyjsonB83d7b77DecodeStudygoEasyjson1(in, &out.School)case "birthday":if data := in.Raw(); in.Ok() {in.AddError((out.Birthday).UnmarshalJSON(data))}default:in.SkipRecursive()}in.WantComma()}in.Delim('}')if isTopLevel {in.Consumed()}}func easyjsonB83d7b77EncodeStudygoEasyjson(out *jwriter.Writer, in Student) {out.RawByte('{')first := true_ = firstif !first {out.RawByte(',')}first = falseout.RawString("\"id\":")out.Int(int(in.Id))if !first {out.RawByte(',')}first = falseout.RawString("\"s_name\":")out.String(string(in.Name))if !first {out.RawByte(',')}first = falseout.RawString("\"s_chool\":")easyjsonB83d7b77EncodeStudygoEasyjson1(out, in.School)if !first {out.RawByte(',')}first = falseout.RawString("\"birthday\":")out.Raw((in.Birthday).MarshalJSON())out.RawByte('}')}// MarshalJSON supports json.Marshaler interfacefunc (v Student) MarshalJSON() ([]byte, error) {w := jwriter.Writer{}easyjsonB83d7b77EncodeStudygoEasyjson(&w, v)return w.Buffer.BuildBytes(), w.Error}// MarshalEasyJSON supports easyjson.Marshaler interfacefunc (v Student) MarshalEasyJSON(w *jwriter.Writer) {easyjsonB83d7b77EncodeStudygoEasyjson(w, v)}// UnmarshalJSON supports json.Unmarshaler interfacefunc (v *Student) UnmarshalJSON(data []byte) error {r := jlexer.Lexer{Data: data}easyjsonB83d7b77DecodeStudygoEasyjson(&r, v)return r.Error()}// UnmarshalEasyJSON supports easyjson.Unmarshaler interfacefunc (v *Student) UnmarshalEasyJSON(l *jlexer.Lexer) {easyjsonB83d7b77DecodeStudygoEasyjson(l, v)}func easyjsonB83d7b77DecodeStudygoEasyjson1(in *jlexer.Lexer, out *School) {isTopLevel := in.IsStart()if in.IsNull() {if isTopLevel {in.Consumed()}in.Skip()return}in.Delim('{')for !in.IsDelim('}') {key := in.UnsafeString()in.WantColon()if in.IsNull() {in.Skip()in.WantComma()continue}switch key {case "name":out.Name = string(in.String())case "addr":out.Addr = string(in.String())default:in.SkipRecursive()}in.WantComma()}in.Delim('}')if isTopLevel {in.Consumed()}}func easyjsonB83d7b77EncodeStudygoEasyjson1(out *jwriter.Writer, in School) {out.RawByte('{')first := true_ = firstif !first {out.RawByte(',')}first = falseout.RawString("\"name\":")out.String(string(in.Name))if !first {out.RawByte(',')}first = falseout.RawString("\"addr\":")out.String(string(in.Addr))out.RawByte('}')}
現在可以寫一個測試類別啦。
package mainimport ( "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}
效能測試對比
Benchmark Results
ffjson results are from February 4th, 2016, using the latest ffjson and go1.6.go/codec results are from March 4th, 2016, using the latest go/codec and go1.6.
Unmarshaling
| lib | json size | MB/s | allocs/op | B/op ||:---------|:----------|-----:|----------:|------:|| standard | regular | 22 | 218 | 10229 || standard | small | 9.7 | 14 | 720 || | | | | || easyjson | regular | 125 | 128 | 9794 || easyjson | small | 67 | 3 | 128 || | | | | || ffjson | regular | 66 | 141 | 9985 || ffjson | small | 17.6 | 10 | 488 || | | | | || codec | regular | 55 | 434 | 19299 || codec | small | 29 | 7 | 336 || | | | | || ujson | regular | 103 | N/A | N/A |
Marshaling, one goroutine.
| lib | json size | MB/s | allocs/op | B/op ||:----------|:----------|-----:|----------:|------:|| standard | regular | 75 | 9 | 23256 || standard | small | 32 | 3 | 328 || standard | large | 80 | 17 | 1.2M || | | | | || easyjson | regular | 213 | 9 | 10260 || easyjson* | regular | 263 | 8 | 742 || easyjson | small | 125 | 1 | 128 || easyjson | large | 212 | 33 | 490k || easyjson* | large | 262 | 25 | 2879 || | | | | || ffjson | regular | 122 | 153 | 21340 || ffjson** | regular | 146 | 152 | 4897 || ffjson | small | 36 | 5 | 384 || ffjson** | small | 64 | 4 | 128 || ffjson | large | 134 | 7317 | 818k || ffjson** | large | 125 | 7320 | 827k || | | | | || codec | regular | 80 | 17 | 33601 || codec*** | regular | 108 | 9 | 1153 || codec | small | 42 | 3 | 304 || codec*** | small | 56 | 1 | 48 || codec | large | 73 | 483 | 2.5M || codec*** | large | 103 | 451 | 66007 || | | | | || ujson | regular | 92 | N/A | N/A |
* marshaling to a writer,** using ffjson.Pool(),*** reusing output slice instead of resetting it to nil
Marshaling, concurrent.
| lib | json size | MB/s | allocs/op | B/op ||:----------|:----------|-----:|----------:|------:|| standard | regular | 252 | 9 | 23257 || standard | small | 124 | 3 | 328 || standard | large | 289 | 17 | 1.2M || | | | | || easyjson | regular | 792 | 9 | 10597 || easyjson* | regular | 1748 | 8 | 779 || easyjson | small | 333 | 1 | 128 || easyjson | large | 718 | 36 | 548k || easyjson* | large | 2134 | 25 | 4957 || | | | | || ffjson | regular | 301 | 153 | 21629 || ffjson** | regular | 707 | 152 | 5148 || ffjson | small | 62 | 5 | 384 || ffjson** | small | 282 | 4 | 128 || ffjson | large | 438 | 7330 | 1.0M || ffjson** | large | 131 | 7319 | 820k || | | | | || codec | regular | 183 | 17 | 33603 || codec*** | regular | 671 | 9 | 1157 || codec | small | 147 | 3 | 304 || codec*** | small | 299 | 1 | 48 || codec | large | 190 | 483 | 2.5M || codec*** | large | 752 | 451 | 77574 |
* marshaling to a writer,** using ffjson.Pool(),*** reusing output slice instead of resetting it to nil