一个用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的主要内容,如果未能解决你的问题,请参考以下文章

用 Go 解析复杂 JSON 的思路

json库在golang中的使用

Go 语言入门很简单 -- Go 语言解析JSON #私藏项目实操分享#

2 Go语言JSON与XML解析与表单操作

2 Go语言JSON与XML解析与表单操作

go语言json处理