GitHub - francoispqt/gojay: highly performant JSON encoder/decoder for Golang
source link: https://github.com/francoispqt/gojay
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
README.md
GoJay
GoJay is a performant JSON encoder/decoder for Golang (currently the most performant, see benchmarks).
It has a simple API and doesn't use reflection. It relies on small interfaces to decode/encode structures and slices.
Gojay also comes with powerful stream decoding features.
Get started
go get github.com/francoispqt/gojay
Decoding
Example of basic stucture decoding:
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":"[email protected]"}`) err := gojay.UnmarshalObject(d, user) if err != nil { log.Fatal(err) } }
Structs
UnmarshalerObject Interface
To unmarshal a JSON object to a structure, the structure must implement the UnmarshalerObject interface:
type UnmarshalerObject interface { UnmarshalObject(*Decoder, string) error NKeys() int }
UnmarshalObject method takes two arguments, the first one is a pointer to the Decoder (*gojay.Decoder) and the second one is the string value of the current key being parsed. If the JSON data is not an object, the UnmarshalObject method will never be called.
NKeys method must return the number of keys to Unmarshal in the JSON object.
Example of implementation:
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 }
Arrays, Slices and Channels
To unmarshal a JSON object to a slice an array or a channel, it must implement the UnmarshalerArray interface:
type UnmarshalerArray interface { UnmarshalArray(*Decoder) error }
UnmarshalArray method takes one argument, a pointer to the Decoder (*gojay.Decoder). If the JSON data is not an array, the Unmarshal method will never be called.
Example of implementation with a slice:
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 }
Example of implementation with a channel:
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 }
Stream Decoding
GoJay ships with a powerful stream decoder.
It allows to read continuously from an io.Reader stream and do JIT decoding writing unmarshalled JSON to a channel to allow async consuming.
When using the Stream API, the Decoder implements context.Context to provide graceful cancellation.
Example:
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") } } }
Other types
To decode other types (string, int, int32, int64, uint32, uint64, float, booleans), you don't need to implement any interface.
Example of encoding strings:
func main() { json := []byte(`"Jay"`) var v string err := Unmarshal(json, &v) if err != nil { log.Fatal(err) } fmt.Println(v) // Jay }
Encoding
Example of basic structure encoding:
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", "[email protected]"} b, _ := gojay.MarshalObject(user) fmt.Println(string(b)) // {"id":1,"name":"gojay","email":"[email protected]"} }
Structs
To encode a structure, the structure must implement the MarshalerObject interface:
type MarshalerObject interface { MarshalObject(enc *Encoder) IsNil() bool }
MarshalObject method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all the keys in the JSON Object by calling Decoder's methods.
IsNil method returns a boolean indicating if the interface underlying value is nil or not. It is used to safely ensure that the underlying value is not nil without using Reflection.
Example of implementation:
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 }
Arrays and Slices
To encode an array or a slice, the slice/array must implement the MarshalerArray interface:
type MarshalerArray interface { MarshalArray(enc *Encoder) }
MarshalArray method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all element in the JSON Array by calling Decoder's methods.
Example of implementation:
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 }
Other types
To encode other types (string, int, float, booleans), you don't need to implement any interface.
Example of encoding strings:
func main() { name := "Jay" b, err := gojay.Marshal(&name) if err != nil { log.Fatal(err) } fmt.Println(string(b)) // "Jay" }
Benchmarks
Benchmarks encode and decode three different data based on size (small, medium, large).
To run benchmark for decoder:
cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/decoder && make bench
To run benchmark for encoder:
cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder && make bench
Benchmark Results
Decode
Small Payload
ns/op bytes/op allocs/op Std Library 4661 496 12 JsonParser 1313 0 0 JsonIter 899 192 5 EasyJson 929 240 2 GoJay 662 112 1
Medium Payload
ns/op bytes/op allocs/op Std Library 30148 2152 496 JsonParser 7793 0 0 EasyJson 7957 232 6 JsonIter 5967 496 44 GoJay 3914 128 7
Large Payload
ns/op bytes/op allocs/op EasyJson 106626 160 2 JsonParser 66813 0 0 JsonIter 87994 6738 329 GoJay 43402 1408 76
Encode
Small Struct
ns/op bytes/op allocs/op Std Library 1280 464 3 EasyJson 871 944 6 JsonIter 866 272 3 GoJay 484 320 2
Medium Struct
ns/op bytes/op allocs/op Std Library 3325 1496 18 EasyJson 1997 1320 19 JsonIter 1939 648 16 GoJay 1196 936 16
Large Struct
ns/op bytes/op allocs/op Std Library 51317 28704 326 JsonIter 35247 14608 320 EasyJson 32053 15474 327 GoJay 27847 27888 326
Contributing
Contributions are welcome :)
If you encounter issues please report it in Github and/or send an email at [email protected]
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK