GoJay是一个用Go语言写的高性能JSON编码/解码工具,本文详细介绍了实现JSON格式编码/解码的结构体代码,以及和其他工具进行性能测试的对比结果。
目前软件包版本为0.9,仍在开发中。
GoJay是用Go语言写的高性能JSON编码/解码工具(目前性能最高,见下面的基准测试)
它有一个简单的API并且不使用反射(reflection)模块。依靠小接口来解码/编码结构和切片。
Gojay还具有强大的流解码功能。
开始
go get github.com/francoispqt/gojay
解码
解码基本结构体的例子:
import "github.com/francoispqt/gojay"
type user struct {
id int
name string
email string
}
// implement UnmarshalerObject
func (u *user) UnmarshalObject(dec *gojay.Decoder, key string) error {
switch key {
case "id":
return dec.AddInt(&u.id)
case "name":
return dec.AddString(&u.name)
case "email":
return dec.AddString(&u.email)
}
return nil
}
func (u *user) NKeys() int {
return 3
}
func main() {
u := &user{}
d := []byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`)
err := gojay.UnmarshalObject(d, user)
if err != nil {
log.Fatal(err)
}
}
或者使用解码 API(需要一个io.Reader)
func main() {
u := &user{}
dec := gojay.NewDecoder(strings.NewReader(`{"id":1,"name":"gojay","email":"gojay@email.com"}`))
err := dec.Decode(u)
if err != nil {
log.Fatal(err)
}
}
结构体
UnmarshalerObject接口
要将JSON对象解编(unmarshal)到结构体中,则结构体必须实现UnmarshalerObject接口:
type UnmarshalerObject interface {
UnmarshalObject(*Decoder, string) error
NKeys() int
}
UnmarshalObject方法有两个参数,第一个是指向解码器(* gojay.Decoder)的指针,第二个是正在解析的当前键的字符串值。 如果JSON数据不是一个对象,则永远不会调用UnmarshalObject方法。
NKeys方法必须在JSON对象中把key的数量返回给Unmarshal。
具体实现的例子:
type user struct {
id int
name string
email string
}
// implement UnmarshalerObject
func (u *user) UnmarshalObject(dec *gojay.Decoder, key string) error {
switch k {
case "id":
return dec.AddInt(&u.id)
case "name":
return dec.AddString(&u.name)
case "email":
return dec.AddString(&u.email)
}
return nil
}
func (u *user) NKeys() int {
return 3
}
数组Array,切片Slice和通道Channel
要将JSON对象解编为切片,数组或通道,必须实现UnmarshalerArray接口:
type UnmarshalerArray interface {
UnmarshalArray(*Decoder) error
}
UnmarshalArray方法需要一个参数,一个指向解码器(* gojay.Decoder)的指针。 如果JSON数据不是数组,Unmarshal方法将永远不会被调用。
实现切片的例子:
type testSlice []string
// implement UnmarshalerArray
func (t *testStringArr) UnmarshalArray(dec *gojay.Decoder) error {
str := ""
if err := dec.AddString(&str); err != nil {
return err
}
*t = append(*t, str)
return nil
}
实现通道的例子:
type ChannelString chan string
// implement UnmarshalerArray
func (c ChannelArray) UnmarshalArray(dec *gojay.Decoder) error {
str := ""
if err := dec.AddString(&str); err != nil {
return err
}
c <- str
return nil
}
流解码
GoJay自带一个强大的流解码器。
它允许从一个io.Reader流中连续读取并进行JIT解码,将未编组的JSON写入一个通道以允许异步消费。
使用Stream API时,解码器(Decoder)实现context.Context以提供方便优雅的取消。
例子:
type ChannelStream chan *TestObj
// implement UnmarshalerStream
func (c ChannelStream) UnmarshalStream(dec *gojay.StreamDecoder) error {
obj := &TestObj{}
if err := dec.AddObject(obj); err != nil {
return err
}
c <- obj
return nil
}
func main() {
// create our channel which will receive our objects
streamChan := ChannelStream(make(chan *TestObj))
// get a reader implementing io.Reader
reader := getAnIOReaderStream()
dec := gojay.Stream.NewDecoder(reader)
// start decoding (will block the goroutine until something is written to the ReadWriter)
go dec.DecodeStream(streamChan)
for {
select {
case v := <-streamChan:
// do something with my TestObj
case <-dec.Done():
os.Exit("finished reading stream")
}
}
}
其他类型
要解码其他类型(string,int,int32,int64,uint32,uint64,float,booleans),不需要实现任何接口。
解码字符串的例子:
func main() {
json := []byte(`"Jay"`)
var v string
err := Unmarshal(json, &v)
if err != nil {
log.Fatal(err)
}
fmt.Println(v) // Jay
}
编码
编码基本结构体的例子:
import "github.com/francoispqt/gojay"
type user struct {
id int
name string
email string
}
// implement MarshalerObject
func (u *user) MarshalObject(dec *gojay.Decoder, key string) {
dec.AddIntKey("id", u.id)
dec.AddStringKey("name", u.name)
dec.AddStringKey("email", u.email)
}
func (u *user) IsNil() bool {
return u == nil
}
func main() {
u := &user{1, "gojay", "gojay@email.com"}
b, _ := gojay.MarshalObject(user)
fmt.Println(string(b)) // {"id":1,"name":"gojay","email":"gojay@email.com"}
}
结构体
为了对结构体进行编码,结构体必须实现MarshalerObject接口:
type MarshalerObject interface {
MarshalObject(enc *Encoder)
IsNil() bool
}
MarshalObject方法需要一个参数,一个指向编码器(* gojay.Encoder)的指针。 该方法必须通过调用解码器的方法来添加JSON对象中的所有关键字。
IsNil方法返回一个布尔值,表明接口底层underlying值是否为零。 它用于在不使用反射Reflection的情况下安全地确保底层值不为零。
实现的例子:
type user struct {
id int
name string
email string
}
// implement MarshalerObject
func (u *user) MarshalObject(dec *gojay.Decoder, key string) {
dec.AddIntKey("id", u.id)
dec.AddStringKey("name", u.name)
dec.AddStringKey("email", u.email)
}
func (u *user) IsNil() bool {
return u == nil
}
数组和切片
要对数组或切片编码,切片/数组必须实现MarshalerArray接口:
type MarshalerArray interface {
MarshalArray(enc *Encoder)
}
MarshalArray方法有一个参数,一个指向编码器(* gojay.Encoder)的指针。该方法必须通过调用解码器的方法来添加JSON数组中的所有元素。
实现的例子:
type users []*user
// implement MarshalerArray
func (u *users) MarshalArray(dec *Decoder) error {
for _, e := range u {
err := enc.AddObject(e)
if err != nil {
return err
}
}
return nil
}
其他类型
要编码其他类型(string,int,float,booleans),不需要实现任何接口。
字符串编码的例子:
func main() {
name := "Jay"
b, err := gojay.Marshal(&name)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b)) // "Jay"
}
基准测试
基准测试根据尺寸(小,中,大)对三种不同的数据进行编码和解码。
运行解码器的基准:
cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/decoder && make bench
运行编码器的基准:
cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder && make bench
基准测试结果
解码
小载荷
ns/op纳秒/操作 | bytes/op节/操作 | allocs/op内存分配/操作 | |
---|---|---|---|
标准库 | 4661 | 496 | 12 |
JsonParser | 1313 | 0 | 0 |
JsonIter | 899 | 192 | 5 |
EasyJson | 929 | 240 | 2 |
GoJay | 662 | 112 | 1 |
中等载荷
ns/op纳秒/操作 | bytes/op字节/操作 | allocs/op内存分配/操作 | |
---|---|---|---|
标准库 | 30148 | 2152 | 496 |
JsonParser | 7793 | 0 | 0 |
EasyJson | 7957 | 232 | 6 |
JsonIter | 5967 | 496 | 44 |
GoJay | 3914 | 128 | 7 |
大载荷
ns/op纳秒/操作 | bytes/op字节/操作 | allocs/op内存分配/操作 | |
---|---|---|---|
EasyJson | 106626 | 160 | 2 |
JsonParser | 66813 | 0 | 0 |
JsonIter | 87994 | 6738 | 329 |
GoJay | 43402 | 1408 | 76 |
编码
小结构体
ns/op纳秒/操作 | bytes/op字节/操作 | allocs/op内存分配/操作 | |
---|---|---|---|
标准库 | 1280 | 464 | 3 |
EasyJson | 871 | 944 | 6 |
JsonIter | 866 | 272 | 3 |
GoJay | 484 | 320 | 2 |
中等结构体
ns/op纳秒/操作 | bytes/op字节/操作 | allocs/op内存分配/操作 | |
---|---|---|---|
标准库 | 3325 | 1496 | 18 |
EasyJson | 1997 | 1320 | 19 |
JsonIter | 1939 | 648 | 16 |
GoJay | 1196 | 936 | 16 |
大结构体
ns/op纳秒/操作 | bytes/op字节/操作 | allocs/op内存分配/操作 | |
---|---|---|---|
标准库 | 51317 | 28704 | 326 |
JsonIter | 35247 | 14608 | 320 |
EasyJson | 32053 | 15474 | 327 |
GoJay | 27847 | 27888 | 326 |
贡献
欢迎贡献您的力量 :)