Go 语言操作 JSON 的几种方法
标准库的 json 模块
Go 语言标准库 encoding/json
提供了操作 JSON 的方法,一般可以使用 json.Marshal
和 json.Unmarshal
来序列化和解析 JSON 字符串:
// 定义结构体 type User struct { Email string `json:"email"` Password string `json:"password"` } // 序列化 buf, err := json.Marshal(User{ Email: "xxxx@example.com", Password: "123456", }) // 解析 user := User {} err := json.Unmarshal([]byte(`{ "email": "xxx@example.com", "password": "123456" }`), &user)
更加灵活和更好性能的 jsoniter 模块
标准库 encoding/json
在使用时需要预先定义结构体,使用时显得不够灵活。这时候可以尝试使用 github.com/json-iterator/go
模块,其除了 提供与标准库一样的接口之外,还提供了一系列更加灵活的操作方法
。
val := []byte(`{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}`) // 仅解析 Colors 字段,并直接得到 string 类型 str := jsoniter.Get(val, "Colors", 0).ToString()
另辟蹊径提高性能的 easyjson 模块
标准库 encoding/json
需要依赖反射来实现,因此性能上会比较差。 github.com/mailru/easyjson
则是利用 go generate
机制自动为结构体生成实现了 MarshalJSON
和 UnmarshalJSON
方法的代码,在序列化和解析时可以直接生成对应字段的 JSON 数据,而不需要运行时反射。据官方的介绍,其性能是标准库的 4~5 倍,是其他 json 模块的 2~3 倍。
要使用 easyjson
模块,首先执行以下命令安装 easyjson
命令:
go get -u github.com/mailru/easyjson/...
然后新建文件 school.go
,并定义结构体 School
:
//easyjson:json type School struct { Name string `json:"name"` Addr string `json:"addr"` }
接着执行 easyjson -all school.go
,此时目录下会生成一个新的文件 school_easyjson.go
,为 School
结构体实现了 MarshalJSON
和 UnmarshalerJSON
方法,接着使用 easyjson 对应的方法去对这个结构体进行解析即可。
简单性能测试结果
对于以上介绍的三个模块,我测试了对于以下 JSON 字符串其序列化和解析性能的测试结果。
测试程序:
package awesomeProject import ( "encoding/json" "testing" jsoniter "github.com/json-iterator/go" "github.com/mailru/easyjson" ) type T1 struct { Name string `json:"name"` Age int `json:"age"` Intro string `json:"intro"` Valid bool `json:"valid"` } // easyjson:json type T2 struct { Name string `json:"name"` Age int `json:"age"` Intro string `json:"intro"` Valid bool `json:"valid"` } var d1 = T1{Name: "如何快速提升", Age: 12320, Intro: "如何快速提升 Go 程序性能?如何快速提升 Go 程序性能?如何快速提升 Go 程序性能?Well, easyJson is 4 times faster than normal json(as per its documets) ,in our organization we have used it extensively and yes its faster. Here is a small example to get started. my current directory name is easyJson", Valid: true} var d2 = T2{Name: "如何快速提升", Age: 12320, Intro: "如何快速提升 Go 程序性能?如何快速提升 Go 程序性能?如何快速提升 Go 程序性能?Well, easyJson is 4 times faster than normal json(as per its documets) ,in our organization we have used it extensively and yes its faster. Here is a small example to get started. my current directory name is easyJson", Valid: true} var s1 []byte func init() { buf, err := json.Marshal(&d1) if err != nil { panic(err) } s1 = buf println(string(s1)) } func BenchmarkStdJsonMarshal(b *testing.B) { i := 0 for i < b.N { i++ _, err := json.Marshal(&d1) if err != nil { panic(err) } } } func BenchmarkStdJsonUnmarshal(b *testing.B) { i := 0 var d T1 for i < b.N { i++ err := json.Unmarshal(s1, &d) if err != nil { panic(err) } } } func BenchmarkJsoniterMarshal(b *testing.B) { i := 0 for i < b.N { i++ _, err := jsoniter.Marshal(&d1) if err != nil { panic(err) } } } func BenchmarkJsoniterUnmarshal(b *testing.B) { i := 0 var d T1 for i < b.N { i++ err := jsoniter.Unmarshal(s1, &d) if err != nil { panic(err) } } } func BenchmarkJsoniterAny(b *testing.B) { i := 0 var d T1 for i < b.N { i++ a := jsoniter.Get(s1) d.Name = a.Get("name").ToString() d.Age = a.Get("age").ToInt() d.Intro = a.Get("intro").ToString() d.Valid = a.Get("valid").ToBool() } } func BenchmarkJsoniterAnyLight(b *testing.B) { i := 0 var d T1 for i < b.N { i++ a := jsoniter.Get(s1) d.Name = a.Get("name").ToString() } } func BenchmarkEasyJsonMarshal(b *testing.B) { i := 0 for i < b.N { i++ _, err := easyjson.Marshal(d2) if err != nil { panic(err) } } } func BenchmarkEasyJsonUnmarshal(b *testing.B) { i := 0 var d T2 for i < b.N { i++ err := easyjson.Unmarshal(s1, &d) if err != nil { panic(err) } } }
JSON 字符串:
{ "name":"如何快速提升", "age":12320, "intro":"如何快速提升 Go 程序性能?如何快速提升 Go 程序性能?如何快速提升 Go 程序性能?Well, easyJson is 4 times faster than normal json(as per its documets) ,in our organization we have used it extensively and yes its faster. Here is a small example to get started. my current directory name is easyJson", "valid":true }
测试结果:
- BenchmarkStdJsonMarshal-4, 使用标准库序列化,1098 ns/op
- BenchmarkStdJsonUnmarshal-4, 使用标准库解析,5006 ns/op
- BenchmarkJsoniterMarshal-4, 使用 jsoniter 序列化,1106 ns/op
- BenchmarkJsoniterUnmarshal-4, 使用 jsoniter 解析,816 ns/op
- BenchmarkJsoniterAny-4,使用 jsoniter 的 Any 解析,4092 ns/op
- BenchmarkJsoniterAnyLight-4,使用 jsoniter 的 Any 解析一个字段,1263 ns/op
- BenchmarkEasyJsonMarshal-4, 使用 easyjson 序列化,1658 ns/op
- BenchmarkEasyJsonUnmarshal-4, 使用 easyjson 解析,952 ns/op
从以上结果可以看到, jsoniter
在序列化和解析时均有比较好的性能, easyjson
次之,标准库 json
则在解析时性能比较差。当然,这并不是一个比较严格的性能测试,比如没有考虑内存分配问题以及多种不同的 JSON 结构和数据长度的测试。但是,
如果综合考虑性能和灵活性, jsoniter
可能是一个不错的选择。