标准库的 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
可能是一个不错的选择。