GoJay是一個(gè)用Go語(yǔ)言寫的高性能JSON編碼/解碼工具,本文詳細(xì)介紹了實(shí)現(xiàn)JSON格式編碼/解碼的結(jié)構(gòu)體代碼喜命,以及和其他工具進(jìn)行性能測(cè)試的對(duì)比結(jié)果沟沙。
目前軟件包版本為0.9,仍在開發(fā)中渊抄。
GoJay是用Go語(yǔ)言寫的高性能JSON編碼/解碼工具(目前性能最高尝胆,見下面的基準(zhǔn)測(cè)試)
它有一個(gè)簡(jiǎn)單的API并且不使用反射(reflection)模塊。依靠小接口來(lái)解碼/編碼結(jié)構(gòu)和切片护桦。
Gojay還具有強(qiáng)大的流解碼功能含衔。
開始
go get github.com/francoispqt/gojay
解碼
解碼基本結(jié)構(gòu)體的例子:
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(需要一個(gè)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)
}
}
結(jié)構(gòu)體
UnmarshalerObject接口
要將JSON對(duì)象解編(unmarshal)到結(jié)構(gòu)體中,則結(jié)構(gòu)體必須實(shí)現(xiàn)UnmarshalerObject接口:
type UnmarshalerObject interface {
UnmarshalObject(*Decoder, string) error
NKeys() int
}
UnmarshalObject方法有兩個(gè)參數(shù)二庵,第一個(gè)是指向解碼器(* gojay.Decoder)的指針贪染,第二個(gè)是正在解析的當(dāng)前鍵的字符串值。 如果JSON數(shù)據(jù)不是一個(gè)對(duì)象催享,則永遠(yuǎn)不會(huì)調(diào)用UnmarshalObject方法杭隙。
NKeys方法必須在JSON對(duì)象中把key的數(shù)量返回給Unmarshal。
具體實(shí)現(xiàn)的例子:
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
}
數(shù)組Array因妙,切片Slice和通道Channel
要將JSON對(duì)象解編為切片痰憎,數(shù)組或通道,必須實(shí)現(xiàn)UnmarshalerArray接口:
type UnmarshalerArray interface {
UnmarshalArray(*Decoder) error
}
UnmarshalArray方法需要一個(gè)參數(shù)攀涵,一個(gè)指向解碼器(* gojay.Decoder)的指針铣耘。 如果JSON數(shù)據(jù)不是數(shù)組,Unmarshal方法將永遠(yuǎn)不會(huì)被調(diào)用以故。
實(shí)現(xiàn)切片的例子:
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
}
實(shí)現(xiàn)通道的例子:
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自帶一個(gè)強(qiáng)大的流解碼器蜗细。
它允許從一個(gè)io.Reader流中連續(xù)讀取并進(jìn)行JIT解碼,將未編組的JSON寫入一個(gè)通道以允許異步消費(fèi)怒详。
使用Stream API時(shí)炉媒,解碼器(Decoder)實(shí)現(xiàn)context.Context以提供方便優(yōu)雅的取消。
例子:
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)蜗元,不需要實(shí)現(xiàn)任何接口或渤。
解碼字符串的例子:
func main() {
json := []byte(`"Jay"`)
var v string
err := Unmarshal(json, &v)
if err != nil {
log.Fatal(err)
}
fmt.Println(v) // Jay
}
編碼
編碼基本結(jié)構(gòu)體的例子:
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"}
}
結(jié)構(gòu)體
為了對(duì)結(jié)構(gòu)體進(jìn)行編碼,結(jié)構(gòu)體必須實(shí)現(xiàn)MarshalerObject接口:
type MarshalerObject interface {
MarshalObject(enc *Encoder)
IsNil() bool
}
MarshalObject方法需要一個(gè)參數(shù)奕扣,一個(gè)指向編碼器(* gojay.Encoder)的指針薪鹦。 該方法必須通過(guò)調(diào)用解碼器的方法來(lái)添加JSON對(duì)象中的所有關(guān)鍵字。
IsNil方法返回一個(gè)布爾值惯豆,表明接口底層underlying值是否為零池磁。 它用于在不使用反射Reflection的情況下安全地確保底層值不為零。
實(shí)現(xiàn)的例子:
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
}
數(shù)組和切片
要對(duì)數(shù)組或切片編碼楷兽,切片/數(shù)組必須實(shí)現(xiàn)MarshalerArray接口:
type MarshalerArray interface {
MarshalArray(enc *Encoder)
}
MarshalArray方法有一個(gè)參數(shù)地熄,一個(gè)指向編碼器(* gojay.Encoder)的指針。該方法必須通過(guò)調(diào)用解碼器的方法來(lái)添加JSON數(shù)組中的所有元素芯杀。
實(shí)現(xiàn)的例子:
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)却特,不需要實(shí)現(xiàn)任何接口。
字符串編碼的例子:
func main() {
name := "Jay"
b, err := gojay.Marshal(&name)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b)) // "Jay"
}
基準(zhǔn)測(cè)試
基準(zhǔn)測(cè)試根據(jù)尺寸(小筛圆,中裂明,大)對(duì)三種不同的數(shù)據(jù)進(jìn)行編碼和解碼。
運(yùn)行解碼器的基準(zhǔn):
cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/decoder && make bench
運(yùn)行編碼器的基準(zhǔn):
cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder && make bench
基準(zhǔn)測(cè)試結(jié)果
解碼
小載荷
基準(zhǔn)測(cè)試代碼
基準(zhǔn)測(cè)試數(shù)據(jù)
ns/op納秒/操作 | bytes/op節(jié)/操作 | allocs/op內(nèi)存分配/操作 | |
---|---|---|---|
標(biāo)準(zhǔn)庫(kù) | 4661 | 496 | 12 |
JsonParser | 1313 | 0 | 0 |
JsonIter | 899 | 192 | 5 |
EasyJson | 929 | 240 | 2 |
GoJay | 662 | 112 | 1 |
中等載荷
基準(zhǔn)測(cè)試代碼
基準(zhǔn)測(cè)試數(shù)據(jù)
ns/op納秒/操作 | bytes/op字節(jié)/操作 | allocs/op內(nèi)存分配/操作 | |
---|---|---|---|
標(biāo)準(zhǔn)庫(kù) | 30148 | 2152 | 496 |
JsonParser | 7793 | 0 | 0 |
EasyJson | 7957 | 232 | 6 |
JsonIter | 5967 | 496 | 44 |
GoJay | 3914 | 128 | 7 |
大載荷
基準(zhǔn)測(cè)試代碼
基準(zhǔn)測(cè)試數(shù)據(jù)
ns/op納秒/操作 | bytes/op字節(jié)/操作 | allocs/op內(nèi)存分配/操作 | |
---|---|---|---|
EasyJson | 106626 | 160 | 2 |
JsonParser | 66813 | 0 | 0 |
JsonIter | 87994 | 6738 | 329 |
GoJay | 43402 | 1408 | 76 |
編碼
小結(jié)構(gòu)體
基準(zhǔn)測(cè)試代碼
基準(zhǔn)測(cè)試數(shù)據(jù)
ns/op納秒/操作 | bytes/op字節(jié)/操作 | allocs/op內(nèi)存分配/操作 | |
---|---|---|---|
標(biāo)準(zhǔn)庫(kù) | 1280 | 464 | 3 |
EasyJson | 871 | 944 | 6 |
JsonIter | 866 | 272 | 3 |
GoJay | 484 | 320 | 2 |
中等結(jié)構(gòu)體
基準(zhǔn)測(cè)試代碼
基準(zhǔn)測(cè)試數(shù)據(jù)
ns/op納秒/操作 | bytes/op字節(jié)/操作 | allocs/op內(nèi)存分配/操作 | |
---|---|---|---|
標(biāo)準(zhǔn)庫(kù) | 3325 | 1496 | 18 |
EasyJson | 1997 | 1320 | 19 |
JsonIter | 1939 | 648 | 16 |
GoJay | 1196 | 936 | 16 |
大結(jié)構(gòu)體
基準(zhǔn)測(cè)試代碼
基準(zhǔn)測(cè)試數(shù)據(jù)
ns/op納秒/操作 | bytes/op字節(jié)/操作 | allocs/op內(nèi)存分配/操作 | |
---|---|---|---|
標(biāo)準(zhǔn)庫(kù) | 51317 | 28704 | 326 |
JsonIter | 35247 | 14608 | 320 |
EasyJson | 32053 | 15474 | 327 |
GoJay | 27847 | 27888 | 326 |
貢獻(xiàn)
歡迎貢獻(xiàn)您的力量 :)