一个用Go语言写的高性能的json解析器:GoJay
Posted 开源一线
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个用Go语言写的高性能的json解析器:GoJay相关的知识,希望对你有一定的参考价值。
目前软件包版本为0.9,仍在开发中。
GoJay是用Go语言写的高性能JSON编码/解码工具(目前性能最高,见下面的基准测试)
它有一个简单的API并且不使用反射(reflection)模块,依靠小接口来解码/编码结构和切片。
Gojay还具有强大的流解码功能。
开始
go getgithub.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)
}
returnnil
}
func (u*user) NKeys() int {
return3
}
funcmain() {
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)
funcmain() {
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接口:
typeUnmarshalerObject 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)
}
returnnil
}
func (u*user) NKeys() int {
return3
}
数组Array,切片Slice和通道Channel
要将JSON对象解编为切片,数组或通道,必须实现UnmarshalerArray接口:
type UnmarshalerArrayinterface {
UnmarshalArray(*Decoder) error
}
UnmarshalArray方法需要一个参数,一个指向解码器(* gojay.Decoder)的指针。 如果JSON数据不是数组,Unmarshal方法将永远不会被调用。
实现切片的例子:
typetestSlice []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)
returnnil
}
实现通道的例子:
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
returnnil
}
流解码
GoJay自带一个强大的流解码器。
它允许从一个io.Reader流中连续读取并进行JIT解码,将未编组的JSON写入一个通道以允许异步消费。
使用StreamAPI时,解码器(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
returnnil
}
funcmain() {
// createour channel which will receive our objects
streamChan := ChannelStream(make(chan *TestObj))
// get areader implementing io.Reader
reader := getAnIOReaderStream()
dec := gojay.Stream.NewDecoder(reader)
// startdecoding (will block the goroutine until something is written to theReadWriter)
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),不需要实现任何接口。
解码字符串的例子:
funcmain() {
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
}
funcmain() {
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 MarshalerObjectinterface {
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 MarshalerArrayinterface {
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
}
}
returnnil
}
其他类型
要编码其他类型(string,int,float,booleans),不需要实现任何接口。
字符串编码的例子:
funcmain() {
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 && makebench
运行编码器的基准:
cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder && makebench
基准测试结果
解码
小载荷
基准测试代码
基准测试数据
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 |
贡献
欢迎贡献您的力量 :)
以上是关于一个用Go语言写的高性能的json解析器:GoJay的主要内容,如果未能解决你的问题,请参考以下文章