1[![Build Status](https://travis-ci.org/francoispqt/gojay.svg?branch=master)](https://travis-ci.org/francoispqt/gojay) 2[![codecov](https://codecov.io/gh/francoispqt/gojay/branch/master/graph/badge.svg)](https://codecov.io/gh/francoispqt/gojay) 3[![Go Report Card](https://goreportcard.com/badge/github.com/francoispqt/gojay)](https://goreportcard.com/report/github.com/francoispqt/gojay) 4[![Go doc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square 5)](https://godoc.org/github.com/francoispqt/gojay) 6![MIT License](https://img.shields.io/badge/license-mit-blue.svg?style=flat-square) 7[![Sourcegraph](https://sourcegraph.com/github.com/francoispqt/gojay/-/badge.svg)](https://sourcegraph.com/github.com/francoispqt/gojay) 8![stability-stable](https://img.shields.io/badge/stability-stable-green.svg) 9 10# GoJay 11 12<img src="https://github.com/francoispqt/gojay/raw/master/gojay.png" width="200px"> 13 14GoJay is a performant JSON encoder/decoder for Golang (currently the most performant, [see benchmarks](#benchmark-results)). 15 16It has a simple API and doesn't use reflection. It relies on small interfaces to decode/encode structures and slices. 17 18Gojay also comes with powerful stream decoding features and an even faster [Unsafe](#unsafe-api) API. 19 20There is also a [code generation tool](https://github.com/francoispqt/gojay/tree/master/gojay) to make usage easier and faster. 21 22# Why another JSON parser? 23 24I looked at other fast decoder/encoder and realised it was mostly hardly readable static code generation or a lot of reflection, poor streaming features, and not so fast in the end. 25 26Also, I wanted to build a decoder that could consume an io.Reader of line or comma delimited JSON, in a JIT way. To consume a flow of JSON objects from a TCP connection for example or from a standard output. Same way I wanted to build an encoder that could encode a flow of data to a io.Writer. 27 28This is how GoJay aims to be a very fast, JIT stream parser with 0 reflection, low allocation with a friendly API. 29 30# Get started 31 32```bash 33go get github.com/francoispqt/gojay 34``` 35 36* [Encoder](#encoding) 37* [Decoder](#decoding) 38* [Stream API](#stream-api) 39* [Code Generation](https://github.com/francoispqt/gojay/tree/master/gojay) 40 41## Decoding 42 43Decoding is done through two different API similar to standard `encoding/json`: 44* [Unmarshal](#unmarshal-api) 45* [Decode](#decode-api) 46 47 48Example of basic stucture decoding with Unmarshal: 49```go 50import "github.com/francoispqt/gojay" 51 52type user struct { 53 id int 54 name string 55 email string 56} 57// implement gojay.UnmarshalerJSONObject 58func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { 59 switch key { 60 case "id": 61 return dec.Int(&u.id) 62 case "name": 63 return dec.String(&u.name) 64 case "email": 65 return dec.String(&u.email) 66 } 67 return nil 68} 69func (u *user) NKeys() int { 70 return 3 71} 72 73func main() { 74 u := &user{} 75 d := []byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`) 76 err := gojay.UnmarshalJSONObject(d, u) 77 if err != nil { 78 log.Fatal(err) 79 } 80} 81``` 82 83with Decode: 84```go 85func main() { 86 u := &user{} 87 dec := gojay.NewDecoder(bytes.NewReader([]byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`))) 88 err := dec.DecodeObject(d, u) 89 if err != nil { 90 log.Fatal(err) 91 } 92} 93``` 94 95### Unmarshal API 96 97Unmarshal API decodes a `[]byte` to a given pointer with a single function. 98 99Behind the doors, Unmarshal API borrows a `*gojay.Decoder` resets its settings and decodes the data to the given pointer and releases the `*gojay.Decoder` to the pool when it finishes, whether it encounters an error or not. 100 101If it cannot find the right Decoding strategy for the type of the given pointer, it returns an `InvalidUnmarshalError`. You can test the error returned by doing `if ok := err.(InvalidUnmarshalError); ok {}`. 102 103Unmarshal API comes with three functions: 104* Unmarshal 105```go 106func Unmarshal(data []byte, v interface{}) error 107``` 108 109* UnmarshalJSONObject 110```go 111func UnmarshalJSONObject(data []byte, v gojay.UnmarshalerJSONObject) error 112``` 113 114* UnmarshalJSONArray 115```go 116func UnmarshalJSONArray(data []byte, v gojay.UnmarshalerJSONArray) error 117``` 118 119 120### Decode API 121 122Decode API decodes a `[]byte` to a given pointer by creating or borrowing a `*gojay.Decoder` with an `io.Reader` and calling `Decode` methods. 123 124__Getting a *gojay.Decoder or Borrowing__ 125 126You can either get a fresh `*gojay.Decoder` calling `dec := gojay.NewDecoder(io.Reader)` or borrow one from the pool by calling `dec := gojay.BorrowDecoder(io.Reader)`. 127 128After using a decoder, you can release it by calling `dec.Release()`. Beware, if you reuse the decoder after releasing it, it will panic with an error of type `InvalidUsagePooledDecoderError`. If you want to fully benefit from the pooling, you must release your decoders after using. 129 130Example getting a fresh an releasing: 131```go 132str := "" 133dec := gojay.NewDecoder(strings.NewReader(`"test"`)) 134defer dec.Release() 135if err := dec.Decode(&str); err != nil { 136 log.Fatal(err) 137} 138``` 139Example borrowing a decoder and releasing: 140```go 141str := "" 142dec := gojay.BorrowDecoder(strings.NewReader(`"test"`)) 143defer dec.Release() 144if err := dec.Decode(&str); err != nil { 145 log.Fatal(err) 146} 147``` 148 149`*gojay.Decoder` has multiple methods to decode to specific types: 150* Decode 151```go 152func (dec *gojay.Decoder) Decode(v interface{}) error 153``` 154* DecodeObject 155```go 156func (dec *gojay.Decoder) DecodeObject(v gojay.UnmarshalerJSONObject) error 157``` 158* DecodeArray 159```go 160func (dec *gojay.Decoder) DecodeArray(v gojay.UnmarshalerJSONArray) error 161``` 162* DecodeInt 163```go 164func (dec *gojay.Decoder) DecodeInt(v *int) error 165``` 166* DecodeBool 167```go 168func (dec *gojay.Decoder) DecodeBool(v *bool) error 169``` 170* DecodeString 171```go 172func (dec *gojay.Decoder) DecodeString(v *string) error 173``` 174 175All DecodeXxx methods are used to decode top level JSON values. If you are decoding keys or items of a JSON object or array, don't use the Decode methods. 176 177Example: 178```go 179reader := strings.NewReader(`"John Doe"`) 180dec := NewDecoder(reader) 181 182var str string 183err := dec.DecodeString(&str) 184if err != nil { 185 log.Fatal(err) 186} 187 188fmt.Println(str) // John Doe 189``` 190 191### Structs and Maps 192#### UnmarshalerJSONObject Interface 193 194To unmarshal a JSON object to a structure, the structure must implement the `UnmarshalerJSONObject` interface: 195```go 196type UnmarshalerJSONObject interface { 197 UnmarshalJSONObject(*gojay.Decoder, string) error 198 NKeys() int 199} 200``` 201`UnmarshalJSONObject` 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 UnmarshalJSONObject method will never be called. 202 203`NKeys` method must return the number of keys to Unmarshal in the JSON object or 0. If zero is returned, all keys will be parsed. 204 205Example of implementation for a struct: 206```go 207type user struct { 208 id int 209 name string 210 email string 211} 212// implement UnmarshalerJSONObject 213func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { 214 switch key { 215 case "id": 216 return dec.Int(&u.id) 217 case "name": 218 return dec.String(&u.name) 219 case "email": 220 return dec.String(&u.email) 221 } 222 return nil 223} 224func (u *user) NKeys() int { 225 return 3 226} 227``` 228 229Example of implementation for a `map[string]string`: 230```go 231// define our custom map type implementing UnmarshalerJSONObject 232type message map[string]string 233 234// Implementing Unmarshaler 235func (m message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { 236 str := "" 237 err := dec.String(&str) 238 if err != nil { 239 return err 240 } 241 m[k] = str 242 return nil 243} 244 245// we return 0, it tells the Decoder to decode all keys 246func (m message) NKeys() int { 247 return 0 248} 249``` 250 251### Arrays, Slices and Channels 252 253To unmarshal a JSON object to a slice an array or a channel, it must implement the UnmarshalerJSONArray interface: 254```go 255type UnmarshalerJSONArray interface { 256 UnmarshalJSONArray(*gojay.Decoder) error 257} 258``` 259UnmarshalJSONArray 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. 260 261Example of implementation with a slice: 262```go 263type testSlice []string 264// implement UnmarshalerJSONArray 265func (t *testSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { 266 str := "" 267 if err := dec.String(&str); err != nil { 268 return err 269 } 270 *t = append(*t, str) 271 return nil 272} 273 274func main() { 275 dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`)) 276 var slice testSlice 277 err := dec.DecodeArray(&slice) 278 if err != nil { 279 log.Fatal(err) 280 } 281 fmt.Println(slice) // [Tom Jim] 282 dec.Release() 283} 284``` 285 286Example of implementation with a channel: 287```go 288type testChannel chan string 289// implement UnmarshalerJSONArray 290func (c testChannel) UnmarshalJSONArray(dec *gojay.Decoder) error { 291 str := "" 292 if err := dec.String(&str); err != nil { 293 return err 294 } 295 c <- str 296 return nil 297} 298 299func main() { 300 dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`)) 301 c := make(testChannel, 2) 302 err := dec.DecodeArray(c) 303 if err != nil { 304 log.Fatal(err) 305 } 306 for i := 0; i < 2; i++ { 307 fmt.Println(<-c) 308 } 309 close(c) 310 dec.Release() 311} 312``` 313 314Example of implementation with an array: 315```go 316type testArray [3]string 317// implement UnmarshalerJSONArray 318func (a *testArray) UnmarshalJSONArray(dec *Decoder) error { 319 var str string 320 if err := dec.String(&str); err != nil { 321 return err 322 } 323 a[dec.Index()] = str 324 return nil 325} 326 327func main() { 328 dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim", "Bob"]`)) 329 var a testArray 330 err := dec.DecodeArray(&a) 331 fmt.Println(a) // [Tom Jim Bob] 332 dec.Release() 333} 334``` 335 336### Other types 337To decode other types (string, int, int32, int64, uint32, uint64, float, booleans), you don't need to implement any interface. 338 339Example of encoding strings: 340```go 341func main() { 342 json := []byte(`"Jay"`) 343 var v string 344 err := gojay.Unmarshal(json, &v) 345 if err != nil { 346 log.Fatal(err) 347 } 348 fmt.Println(v) // Jay 349} 350``` 351 352### Decode values methods 353When decoding a JSON object of a JSON array using `UnmarshalerJSONObject` or `UnmarshalerJSONArray` interface, the `gojay.Decoder` provides dozens of methods to Decode multiple types. 354 355Non exhaustive list of methods available (to see all methods, check the godoc): 356```go 357dec.Int 358dec.Int8 359dec.Int16 360dec.Int32 361dec.Int64 362dec.Uint8 363dec.Uint16 364dec.Uint32 365dec.Uint64 366dec.String 367dec.Time 368dec.Bool 369dec.SQLNullString 370dec.SQLNullInt64 371``` 372 373 374## Encoding 375 376Encoding is done through two different API similar to standard `encoding/json`: 377* [Marshal](#marshal-api) 378* [Encode](#encode-api) 379 380Example of basic structure encoding with Marshal: 381```go 382import "github.com/francoispqt/gojay" 383 384type user struct { 385 id int 386 name string 387 email string 388} 389 390// implement MarshalerJSONObject 391func (u *user) MarshalJSONObject(enc *gojay.Encoder) { 392 enc.IntKey("id", u.id) 393 enc.StringKey("name", u.name) 394 enc.StringKey("email", u.email) 395} 396func (u *user) IsNil() bool { 397 return u == nil 398} 399 400func main() { 401 u := &user{1, "gojay", "gojay@email.com"} 402 b, err := gojay.MarshalJSONObject(u) 403 if err != nil { 404 log.Fatal(err) 405 } 406 fmt.Println(string(b)) // {"id":1,"name":"gojay","email":"gojay@email.com"} 407} 408``` 409 410with Encode: 411```go 412func main() { 413 u := &user{1, "gojay", "gojay@email.com"} 414 b := strings.Builder{} 415 enc := gojay.NewEncoder(&b) 416 if err := enc.Encode(u); err != nil { 417 log.Fatal(err) 418 } 419 fmt.Println(b.String()) // {"id":1,"name":"gojay","email":"gojay@email.com"} 420} 421``` 422 423### Marshal API 424 425Marshal API encodes a value to a JSON `[]byte` with a single function. 426 427Behind the doors, Marshal API borrows a `*gojay.Encoder` resets its settings and encodes the data to an internal byte buffer and releases the `*gojay.Encoder` to the pool when it finishes, whether it encounters an error or not. 428 429If it cannot find the right Encoding strategy for the type of the given value, it returns an `InvalidMarshalError`. You can test the error returned by doing `if ok := err.(InvalidMarshalError); ok {}`. 430 431Marshal API comes with three functions: 432* Marshal 433```go 434func Marshal(v interface{}) ([]byte, error) 435``` 436 437* MarshalJSONObject 438```go 439func MarshalJSONObject(v gojay.MarshalerJSONObject) ([]byte, error) 440``` 441 442* MarshalJSONArray 443```go 444func MarshalJSONArray(v gojay.MarshalerJSONArray) ([]byte, error) 445``` 446 447### Encode API 448 449Encode API decodes a value to JSON by creating or borrowing a `*gojay.Encoder` sending it to an `io.Writer` and calling `Encode` methods. 450 451__Getting a *gojay.Encoder or Borrowing__ 452 453You can either get a fresh `*gojay.Encoder` calling `enc := gojay.NewEncoder(io.Writer)` or borrow one from the pool by calling `enc := gojay.BorrowEncoder(io.Writer)`. 454 455After using an encoder, you can release it by calling `enc.Release()`. Beware, if you reuse the encoder after releasing it, it will panic with an error of type `InvalidUsagePooledEncoderError`. If you want to fully benefit from the pooling, you must release your encoders after using. 456 457Example getting a fresh encoder an releasing: 458```go 459str := "test" 460b := strings.Builder{} 461enc := gojay.NewEncoder(&b) 462defer enc.Release() 463if err := enc.Encode(str); err != nil { 464 log.Fatal(err) 465} 466``` 467Example borrowing an encoder and releasing: 468```go 469str := "test" 470b := strings.Builder{} 471enc := gojay.BorrowEncoder(b) 472defer enc.Release() 473if err := enc.Encode(str); err != nil { 474 log.Fatal(err) 475} 476``` 477 478`*gojay.Encoder` has multiple methods to encoder specific types to JSON: 479* Encode 480```go 481func (enc *gojay.Encoder) Encode(v interface{}) error 482``` 483* EncodeObject 484```go 485func (enc *gojay.Encoder) EncodeObject(v gojay.MarshalerJSONObject) error 486``` 487* EncodeArray 488```go 489func (enc *gojay.Encoder) EncodeArray(v gojay.MarshalerJSONArray) error 490``` 491* EncodeInt 492```go 493func (enc *gojay.Encoder) EncodeInt(n int) error 494``` 495* EncodeInt64 496```go 497func (enc *gojay.Encoder) EncodeInt64(n int64) error 498``` 499* EncodeFloat 500```go 501func (enc *gojay.Encoder) EncodeFloat(n float64) error 502``` 503* EncodeBool 504```go 505func (enc *gojay.Encoder) EncodeBool(v bool) error 506``` 507* EncodeString 508```go 509func (enc *gojay.Encoder) EncodeString(s string) error 510``` 511 512### Structs and Maps 513 514To encode a structure, the structure must implement the MarshalerJSONObject interface: 515```go 516type MarshalerJSONObject interface { 517 MarshalJSONObject(enc *gojay.Encoder) 518 IsNil() bool 519} 520``` 521`MarshalJSONObject` 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. 522 523IsNil 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. 524 525Example of implementation for a struct: 526```go 527type user struct { 528 id int 529 name string 530 email string 531} 532 533// implement MarshalerJSONObject 534func (u *user) MarshalJSONObject(enc *gojay.Encoder) { 535 enc.IntKey("id", u.id) 536 enc.StringKey("name", u.name) 537 enc.StringKey("email", u.email) 538} 539func (u *user) IsNil() bool { 540 return u == nil 541} 542``` 543 544Example of implementation for a `map[string]string`: 545```go 546// define our custom map type implementing MarshalerJSONObject 547type message map[string]string 548 549// Implementing Marshaler 550func (m message) MarshalJSONObject(enc *gojay.Encoder) { 551 for k, v := range m { 552 enc.StringKey(k, v) 553 } 554} 555 556func (m message) IsNil() bool { 557 return m == nil 558} 559``` 560 561### Arrays and Slices 562To encode an array or a slice, the slice/array must implement the MarshalerJSONArray interface: 563```go 564type MarshalerJSONArray interface { 565 MarshalJSONArray(enc *gojay.Encoder) 566 IsNil() bool 567} 568``` 569`MarshalJSONArray` 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. 570 571`IsNil` method returns a boolean indicating if the interface underlying value is nil(empty) or not. It is used to safely ensure that the underlying value is not nil without using Reflection and also to in `OmitEmpty` feature. 572 573Example of implementation: 574```go 575type users []*user 576// implement MarshalerJSONArray 577func (u *users) MarshalJSONArray(enc *gojay.Encoder) { 578 for _, e := range u { 579 enc.Object(e) 580 } 581} 582func (u *users) IsNil() bool { 583 return len(u) == 0 584} 585``` 586 587### Other types 588To encode other types (string, int, float, booleans), you don't need to implement any interface. 589 590Example of encoding strings: 591```go 592func main() { 593 name := "Jay" 594 b, err := gojay.Marshal(name) 595 if err != nil { 596 log.Fatal(err) 597 } 598 fmt.Println(string(b)) // "Jay" 599} 600``` 601 602# Stream API 603 604### Stream Decoding 605GoJay ships with a powerful stream decoder. 606 607It allows to read continuously from an io.Reader stream and do JIT decoding writing unmarshalled JSON to a channel to allow async consuming. 608 609When using the Stream API, the Decoder implements context.Context to provide graceful cancellation. 610 611To decode a stream of JSON, you must call `gojay.Stream.DecodeStream` and pass it a `UnmarshalerStream` implementation. 612 613```go 614type UnmarshalerStream interface { 615 UnmarshalStream(*StreamDecoder) error 616} 617``` 618 619Example of implementation of stream reading from a WebSocket connection: 620```go 621// implement UnmarshalerStream 622type ChannelStream chan *user 623 624func (c ChannelStream) UnmarshalStream(dec *gojay.StreamDecoder) error { 625 u := &user{} 626 if err := dec.Object(u); err != nil { 627 return err 628 } 629 c <- u 630 return nil 631} 632 633func main() { 634 // get our websocket connection 635 origin := "http://localhost/" 636 url := "ws://localhost:12345/ws" 637 ws, err := websocket.Dial(url, "", origin) 638 if err != nil { 639 log.Fatal(err) 640 } 641 // create our channel which will receive our objects 642 streamChan := ChannelStream(make(chan *user)) 643 // borrow a decoder 644 dec := gojay.Stream.BorrowDecoder(ws) 645 // start decoding, it will block until a JSON message is decoded from the WebSocket 646 // or until Done channel is closed 647 go dec.DecodeStream(streamChan) 648 for { 649 select { 650 case v := <-streamChan: 651 // Got something from my websocket! 652 log.Println(v) 653 case <-dec.Done(): 654 log.Println("finished reading from WebSocket") 655 os.Exit(0) 656 } 657 } 658} 659``` 660 661### Stream Encoding 662GoJay ships with a powerful stream encoder part of the Stream API. 663 664It allows to write continuously to an io.Writer and do JIT encoding of data fed to a channel to allow async consuming. You can set multiple consumers on the channel to be as performant as possible. Consumers are non blocking and are scheduled individually in their own go routine. 665 666When using the Stream API, the Encoder implements context.Context to provide graceful cancellation. 667 668To encode a stream of data, you must call `EncodeStream` and pass it a `MarshalerStream` implementation. 669 670```go 671type MarshalerStream interface { 672 MarshalStream(enc *gojay.StreamEncoder) 673} 674``` 675 676Example of implementation of stream writing to a WebSocket: 677```go 678// Our structure which will be pushed to our stream 679type user struct { 680 id int 681 name string 682 email string 683} 684 685func (u *user) MarshalJSONObject(enc *gojay.Encoder) { 686 enc.IntKey("id", u.id) 687 enc.StringKey("name", u.name) 688 enc.StringKey("email", u.email) 689} 690func (u *user) IsNil() bool { 691 return u == nil 692} 693 694// Our MarshalerStream implementation 695type StreamChan chan *user 696 697func (s StreamChan) MarshalStream(enc *gojay.StreamEncoder) { 698 select { 699 case <-enc.Done(): 700 return 701 case o := <-s: 702 enc.Object(o) 703 } 704} 705 706// Our main function 707func main() { 708 // get our websocket connection 709 origin := "http://localhost/" 710 url := "ws://localhost:12345/ws" 711 ws, err := websocket.Dial(url, "", origin) 712 if err != nil { 713 log.Fatal(err) 714 } 715 // we borrow an encoder set stdout as the writer, 716 // set the number of consumer to 10 717 // and tell the encoder to separate each encoded element 718 // added to the channel by a new line character 719 enc := gojay.Stream.BorrowEncoder(ws).NConsumer(10).LineDelimited() 720 // instantiate our MarshalerStream 721 s := StreamChan(make(chan *user)) 722 // start the stream encoder 723 // will block its goroutine until enc.Cancel(error) is called 724 // or until something is written to the channel 725 go enc.EncodeStream(s) 726 // write to our MarshalerStream 727 for i := 0; i < 1000; i++ { 728 s <- &user{i, "username", "user@email.com"} 729 } 730 // Wait 731 <-enc.Done() 732} 733``` 734 735# Unsafe API 736 737Unsafe API has the same functions than the regular API, it only has `Unmarshal API` for now. It is unsafe because it makes assumptions on the quality of the given JSON. 738 739If you are not sure if your JSON is valid, don't use the Unsafe API. 740 741Also, the `Unsafe` API does not copy the buffer when using Unmarshal API, which, in case of string decoding, can lead to data corruption if a byte buffer is reused. Using the `Decode` API makes `Unsafe` API safer as the io.Reader relies on `copy` builtin method and `Decoder` will have its own internal buffer :) 742 743Access the `Unsafe` API this way: 744```go 745gojay.Unsafe.Unmarshal(b, v) 746``` 747 748 749# Benchmarks 750 751Benchmarks encode and decode three different data based on size (small, medium, large). 752 753To run benchmark for decoder: 754```bash 755cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/decoder && make bench 756``` 757 758To run benchmark for encoder: 759```bash 760cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder && make bench 761``` 762 763# Benchmark Results 764## Decode 765 766<img src="https://images2.imgbox.com/78/01/49OExcPh_o.png" width="500px"> 767 768### Small Payload 769[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_small_test.go) 770 771[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go) 772 773| | ns/op | bytes/op | allocs/op | 774|-----------------|-----------|--------------|-----------| 775| Std Library | 2547 | 496 | 4 | 776| JsonIter | 2046 | 312 | 12 | 777| JsonParser | 1408 | 0 | 0 | 778| EasyJson | 929 | 240 | 2 | 779| **GoJay** | **807** | **256** | **2** | 780| **GoJay-unsafe**| **712** | **112** | **1** | 781 782### Medium Payload 783[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_medium_test.go) 784 785[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go) 786 787| | ns/op | bytes/op | allocs/op | 788|-----------------|-----------|----------|-----------| 789| Std Library | 30148 | 2152 | 496 | 790| JsonIter | 16309 | 2976 | 80 | 791| JsonParser | 7793 | 0 | 0 | 792| EasyJson | 7957 | 232 | 6 | 793| **GoJay** | **4984** | **2448** | **8** | 794| **GoJay-unsafe**| **4809** | **144** | **7** | 795 796### Large Payload 797[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_large_test.go) 798 799[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go) 800 801| | ns/op | bytes/op | allocs/op | 802|-----------------|-----------|-------------|-----------| 803| JsonIter | 210078 | 41712 | 1136 | 804| EasyJson | 106626 | 160 | 2 | 805| JsonParser | 66813 | 0 | 0 | 806| **GoJay** | **52153** | **31241** | **77** | 807| **GoJay-unsafe**| **48277** | **2561** | **76** | 808 809## Encode 810 811<img src="https://images2.imgbox.com/e9/cc/pnM8c7Gf_o.png" width="500px"> 812 813### Small Struct 814[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_small_test.go) 815 816[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go) 817 818| | ns/op | bytes/op | allocs/op | 819|----------------|----------|--------------|-----------| 820| Std Library | 1280 | 464 | 3 | 821| EasyJson | 871 | 944 | 6 | 822| JsonIter | 866 | 272 | 3 | 823| **GoJay** | **543** | **112** | **1** | 824| **GoJay-func** | **347** | **0** | **0** | 825 826### Medium Struct 827[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_medium_test.go) 828 829[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go) 830 831| | ns/op | bytes/op | allocs/op | 832|-------------|----------|--------------|-----------| 833| Std Library | 5006 | 1496 | 25 | 834| JsonIter | 2232 | 1544 | 20 | 835| EasyJson | 1997 | 1544 | 19 | 836| **GoJay** | **1522** | **312** | **14** | 837 838### Large Struct 839[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_large_test.go) 840 841[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go) 842 843| | ns/op | bytes/op | allocs/op | 844|-------------|-----------|--------------|-----------| 845| Std Library | 66441 | 20576 | 332 | 846| JsonIter | 35247 | 20255 | 328 | 847| EasyJson | 32053 | 15474 | 327 | 848| **GoJay** | **27847** | **9802** | **318** | 849 850# Contributing 851 852Contributions are welcome :) 853 854If you encounter issues please report it in Github and/or send an email at [francois@parquet.ninja](mailto:francois@parquet.ninja) 855 856