{"id":13601518,"url":"https://github.com/francoispqt/gojay","last_synced_at":"2025-05-13T19:15:13.962Z","repository":{"id":41258131,"uuid":"131022265","full_name":"francoispqt/gojay","owner":"francoispqt","description":"high performance JSON encoder/decoder with stream API for Golang ","archived":false,"fork":false,"pushed_at":"2023-11-22T16:50:12.000Z","size":5880,"stargazers_count":2130,"open_issues_count":49,"forks_count":114,"subscribers_count":35,"default_branch":"master","last_synced_at":"2025-04-27T20:03:29.648Z","etag":null,"topics":["decoder","encoder","go","golang","json","perfomance","stream-decoder","stream-processing"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/francoispqt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2018-04-25T14:52:24.000Z","updated_at":"2025-04-16T21:36:24.000Z","dependencies_parsed_at":"2024-02-24T07:31:55.683Z","dependency_job_id":"3af75b20-91b2-419c-b683-b1d7abba3c18","html_url":"https://github.com/francoispqt/gojay","commit_stats":{"total_commits":233,"total_committers":20,"mean_commits":11.65,"dds":"0.12446351931330468","last_synced_commit":"1398296d938f9fae26750ddc2fe356b6d897f799"},"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoispqt%2Fgojay","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoispqt%2Fgojay/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoispqt%2Fgojay/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoispqt%2Fgojay/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/francoispqt","download_url":"https://codeload.github.com/francoispqt/gojay/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254010823,"owners_count":21999001,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["decoder","encoder","go","golang","json","perfomance","stream-decoder","stream-processing"],"created_at":"2024-08-01T18:01:03.907Z","updated_at":"2025-05-13T19:15:13.941Z","avatar_url":"https://github.com/francoispqt.png","language":"Go","funding_links":[],"categories":["Misc","Go","Repositories"],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/francoispqt/gojay.svg?branch=master)](https://travis-ci.org/francoispqt/gojay)\n[![codecov](https://codecov.io/gh/francoispqt/gojay/branch/master/graph/badge.svg)](https://codecov.io/gh/francoispqt/gojay)\n[![Go Report Card](https://goreportcard.com/badge/github.com/francoispqt/gojay)](https://goreportcard.com/report/github.com/francoispqt/gojay)\n[![Go doc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square\n)](https://godoc.org/github.com/francoispqt/gojay)\n![MIT License](https://img.shields.io/badge/license-mit-blue.svg?style=flat-square)\n[![Sourcegraph](https://sourcegraph.com/github.com/francoispqt/gojay/-/badge.svg)](https://sourcegraph.com/github.com/francoispqt/gojay)\n![stability-stable](https://img.shields.io/badge/stability-stable-green.svg)\n\n# GoJay\n\n\u003cimg src=\"https://github.com/francoispqt/gojay/raw/master/gojay.png\" width=\"200px\"\u003e\n\nGoJay is a performant JSON encoder/decoder for Golang (currently the most performant, [see benchmarks](#benchmark-results)).\n\nIt has a simple API and doesn't use reflection. It relies on small interfaces to decode/encode structures and slices.\n\nGojay also comes with powerful stream decoding features and an even faster [Unsafe](#unsafe-api) API.\n\nThere is also a [code generation tool](https://github.com/francoispqt/gojay/tree/master/gojay) to make usage easier and faster.\n\n# Why another JSON parser?\n\nI 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.\n\nAlso, 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.\n\nThis is how GoJay aims to be a very fast, JIT stream parser with 0 reflection, low allocation with a friendly API.\n\n# Get started\n\n```bash\ngo get github.com/francoispqt/gojay\n```\n\n* [Encoder](#encoding)\n* [Decoder](#decoding)\n* [Stream API](#stream-api)\n* [Code Generation](https://github.com/francoispqt/gojay/tree/master/gojay)\n\n## Decoding\n\nDecoding is done through two different API similar to standard `encoding/json`:\n* [Unmarshal](#unmarshal-api)\n* [Decode](#decode-api)\n\n\nExample of basic stucture decoding with Unmarshal:\n```go\nimport \"github.com/francoispqt/gojay\"\n\ntype user struct {\n    id int\n    name string\n    email string\n}\n// implement gojay.UnmarshalerJSONObject\nfunc (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {\n    switch key {\n    case \"id\":\n        return dec.Int(\u0026u.id)\n    case \"name\":\n        return dec.String(\u0026u.name)\n    case \"email\":\n        return dec.String(\u0026u.email)\n    }\n    return nil\n}\nfunc (u *user) NKeys() int {\n    return 3\n}\n\nfunc main() {\n    u := \u0026user{}\n    d := []byte(`{\"id\":1,\"name\":\"gojay\",\"email\":\"gojay@email.com\"}`)\n    err := gojay.UnmarshalJSONObject(d, u)\n    if err != nil {\n        log.Fatal(err)\n    }\n}\n```\n\nwith Decode:\n```go\nfunc main() {\n    u := \u0026user{}\n    dec := gojay.NewDecoder(bytes.NewReader([]byte(`{\"id\":1,\"name\":\"gojay\",\"email\":\"gojay@email.com\"}`)))\n    err := dec.DecodeObject(d, u)\n    if err != nil {\n        log.Fatal(err)\n    }\n}\n```\n\n### Unmarshal API\n\nUnmarshal API decodes a `[]byte` to a given pointer with a single function.\n\nBehind 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.\n\nIf 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 {}`.\n\nUnmarshal API comes with three functions:\n* Unmarshal\n```go\nfunc Unmarshal(data []byte, v interface{}) error\n```\n\n* UnmarshalJSONObject\n```go\nfunc UnmarshalJSONObject(data []byte, v gojay.UnmarshalerJSONObject) error\n```\n\n* UnmarshalJSONArray\n```go\nfunc UnmarshalJSONArray(data []byte, v gojay.UnmarshalerJSONArray) error\n```\n\n\n### Decode API\n\nDecode API decodes a `[]byte` to a given pointer by creating or borrowing a `*gojay.Decoder` with an `io.Reader` and calling `Decode` methods.\n\n__Getting a *gojay.Decoder or Borrowing__\n\nYou 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)`.\n\nAfter 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.\n\nExample getting a fresh an releasing:\n```go\nstr := \"\"\ndec := gojay.NewDecoder(strings.NewReader(`\"test\"`))\ndefer dec.Release()\nif err := dec.Decode(\u0026str); err != nil {\n    log.Fatal(err)\n}\n```\nExample borrowing a decoder and releasing:\n```go\nstr := \"\"\ndec := gojay.BorrowDecoder(strings.NewReader(`\"test\"`))\ndefer dec.Release()\nif err := dec.Decode(\u0026str); err != nil {\n    log.Fatal(err)\n}\n```\n\n`*gojay.Decoder` has multiple methods to decode to specific types:\n* Decode\n```go\nfunc (dec *gojay.Decoder) Decode(v interface{}) error\n```\n* DecodeObject\n```go\nfunc (dec *gojay.Decoder) DecodeObject(v gojay.UnmarshalerJSONObject) error\n```\n* DecodeArray\n```go\nfunc (dec *gojay.Decoder) DecodeArray(v gojay.UnmarshalerJSONArray) error\n```\n* DecodeInt\n```go\nfunc (dec *gojay.Decoder) DecodeInt(v *int) error\n```\n* DecodeBool\n```go\nfunc (dec *gojay.Decoder) DecodeBool(v *bool) error\n```\n* DecodeString\n```go\nfunc (dec *gojay.Decoder) DecodeString(v *string) error\n```\n\nAll 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.\n\nExample:\n```go\nreader := strings.NewReader(`\"John Doe\"`)\ndec := NewDecoder(reader)\n\nvar str string\nerr := dec.DecodeString(\u0026str)\nif err != nil {\n    log.Fatal(err)\n}\n\nfmt.Println(str) // John Doe\n```\n\n### Structs and Maps\n#### UnmarshalerJSONObject Interface\n\nTo unmarshal a JSON object to a structure, the structure must implement the `UnmarshalerJSONObject` interface:\n```go\ntype UnmarshalerJSONObject interface {\n\tUnmarshalJSONObject(*gojay.Decoder, string) error\n\tNKeys() int\n}\n```\n`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.\n\n`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.\n\nExample of implementation for a struct:\n```go\ntype user struct {\n    id int\n    name string\n    email string\n}\n// implement UnmarshalerJSONObject\nfunc (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {\n    switch key {\n    case \"id\":\n        return dec.Int(\u0026u.id)\n    case \"name\":\n        return dec.String(\u0026u.name)\n    case \"email\":\n        return dec.String(\u0026u.email)\n    }\n    return nil\n}\nfunc (u *user) NKeys() int {\n    return 3\n}\n```\n\nExample of implementation for a `map[string]string`:\n```go\n// define our custom map type implementing UnmarshalerJSONObject\ntype message map[string]string\n\n// Implementing Unmarshaler\nfunc (m message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {\n\tstr := \"\"\n\terr := dec.String(\u0026str)\n\tif err != nil {\n\t\treturn err\n\t}\n\tm[k] = str\n\treturn nil\n}\n\n// we return 0, it tells the Decoder to decode all keys\nfunc (m message) NKeys() int {\n\treturn 0\n}\n```\n\n### Arrays, Slices and Channels\n\nTo unmarshal a JSON object to a slice an array or a channel, it must implement the UnmarshalerJSONArray interface:\n```go\ntype UnmarshalerJSONArray interface {\n\tUnmarshalJSONArray(*gojay.Decoder) error\n}\n```\nUnmarshalJSONArray 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.\n\nExample of implementation with a slice:\n```go\ntype testSlice []string\n// implement UnmarshalerJSONArray\nfunc (t *testSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {\n\tstr := \"\"\n\tif err := dec.String(\u0026str); err != nil {\n\t\treturn err\n\t}\n\t*t = append(*t, str)\n\treturn nil\n}\n\nfunc main() {\n\tdec := gojay.BorrowDecoder(strings.NewReader(`[\"Tom\", \"Jim\"]`))\n\tvar slice testSlice\n\terr := dec.DecodeArray(\u0026slice)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Println(slice) // [Tom Jim]\n\tdec.Release()\n}\n```\n\nExample of implementation with a channel:\n```go\ntype testChannel chan string\n// implement UnmarshalerJSONArray\nfunc (c testChannel) UnmarshalJSONArray(dec *gojay.Decoder) error {\n\tstr := \"\"\n\tif err := dec.String(\u0026str); err != nil {\n\t\treturn err\n\t}\n\tc \u003c- str\n\treturn nil\n}\n\nfunc main() {\n\tdec := gojay.BorrowDecoder(strings.NewReader(`[\"Tom\", \"Jim\"]`))\n\tc := make(testChannel, 2)\n\terr := dec.DecodeArray(c)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfor i := 0; i \u003c 2; i++ {\n\t\tfmt.Println(\u003c-c)\n\t}\n\tclose(c)\n\tdec.Release()\n}\n```\n\nExample of implementation with an array:\n```go\ntype testArray [3]string\n// implement UnmarshalerJSONArray\nfunc (a *testArray) UnmarshalJSONArray(dec *Decoder) error {\n\tvar str string\n\tif err := dec.String(\u0026str); err != nil {\n\t\treturn err\n\t}\n\ta[dec.Index()] = str\n\treturn nil\n}\n\nfunc main() {\n\tdec := gojay.BorrowDecoder(strings.NewReader(`[\"Tom\", \"Jim\", \"Bob\"]`))\n\tvar a testArray\n\terr := dec.DecodeArray(\u0026a)\n\tfmt.Println(a) // [Tom Jim Bob]\n\tdec.Release()\n}\n```\n\n### Other types\nTo decode other types (string, int, int32, int64, uint32, uint64, float, booleans), you don't need to implement any interface.\n\nExample of encoding strings:\n```go\nfunc main() {\n    json := []byte(`\"Jay\"`)\n    var v string\n    err := gojay.Unmarshal(json, \u0026v)\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Println(v) // Jay\n}\n```\n\n### Decode values methods\nWhen decoding a JSON object of a JSON array using `UnmarshalerJSONObject` or `UnmarshalerJSONArray` interface, the `gojay.Decoder` provides dozens of methods to Decode multiple types.\n\nNon exhaustive list of methods available (to see all methods, check the godoc):\n```go\ndec.Int\ndec.Int8\ndec.Int16\ndec.Int32\ndec.Int64\ndec.Uint8\ndec.Uint16\ndec.Uint32\ndec.Uint64\ndec.String\ndec.Time\ndec.Bool\ndec.SQLNullString\ndec.SQLNullInt64\n```\n\n\n## Encoding\n\nEncoding is done through two different API similar to standard `encoding/json`:\n* [Marshal](#marshal-api)\n* [Encode](#encode-api)\n\nExample of basic structure encoding with Marshal:\n```go\nimport \"github.com/francoispqt/gojay\"\n\ntype user struct {\n\tid    int\n\tname  string\n\temail string\n}\n\n// implement MarshalerJSONObject\nfunc (u *user) MarshalJSONObject(enc *gojay.Encoder) {\n\tenc.IntKey(\"id\", u.id)\n\tenc.StringKey(\"name\", u.name)\n\tenc.StringKey(\"email\", u.email)\n}\nfunc (u *user) IsNil() bool {\n\treturn u == nil\n}\n\nfunc main() {\n\tu := \u0026user{1, \"gojay\", \"gojay@email.com\"}\n\tb, err := gojay.MarshalJSONObject(u)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Println(string(b)) // {\"id\":1,\"name\":\"gojay\",\"email\":\"gojay@email.com\"}\n}\n```\n\nwith Encode:\n```go\nfunc main() {\n\tu := \u0026user{1, \"gojay\", \"gojay@email.com\"}\n\tb := strings.Builder{}\n\tenc := gojay.NewEncoder(\u0026b)\n\tif err := enc.Encode(u); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Println(b.String()) // {\"id\":1,\"name\":\"gojay\",\"email\":\"gojay@email.com\"}\n}\n```\n\n### Marshal API\n\nMarshal API encodes a value to a JSON `[]byte` with a single function.\n\nBehind 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.\n\nIf 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 {}`.\n\nMarshal API comes with three functions:\n* Marshal\n```go\nfunc Marshal(v interface{}) ([]byte, error)\n```\n\n* MarshalJSONObject\n```go\nfunc MarshalJSONObject(v gojay.MarshalerJSONObject) ([]byte, error)\n```\n\n* MarshalJSONArray\n```go\nfunc MarshalJSONArray(v gojay.MarshalerJSONArray) ([]byte, error)\n```\n\n### Encode API\n\nEncode API decodes a value to JSON by creating or borrowing a `*gojay.Encoder` sending it to an `io.Writer` and calling `Encode` methods.\n\n__Getting a *gojay.Encoder or Borrowing__\n\nYou 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)`.\n\nAfter 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.\n\nExample getting a fresh encoder an releasing:\n```go\nstr := \"test\"\nb := strings.Builder{}\nenc := gojay.NewEncoder(\u0026b)\ndefer enc.Release()\nif err := enc.Encode(str); err != nil {\n    log.Fatal(err)\n}\n```\nExample borrowing an encoder and releasing:\n```go\nstr := \"test\"\nb := strings.Builder{}\nenc := gojay.BorrowEncoder(b)\ndefer enc.Release()\nif err := enc.Encode(str); err != nil {\n    log.Fatal(err)\n}\n```\n\n`*gojay.Encoder` has multiple methods to encoder specific types to JSON:\n* Encode\n```go\nfunc (enc *gojay.Encoder) Encode(v interface{}) error\n```\n* EncodeObject\n```go\nfunc (enc *gojay.Encoder) EncodeObject(v gojay.MarshalerJSONObject) error\n```\n* EncodeArray\n```go\nfunc (enc *gojay.Encoder) EncodeArray(v gojay.MarshalerJSONArray) error\n```\n* EncodeInt\n```go\nfunc (enc *gojay.Encoder) EncodeInt(n int) error\n```\n* EncodeInt64\n```go\nfunc (enc *gojay.Encoder) EncodeInt64(n int64) error\n```\n* EncodeFloat\n```go\nfunc (enc *gojay.Encoder) EncodeFloat(n float64) error\n```\n* EncodeBool\n```go\nfunc (enc *gojay.Encoder) EncodeBool(v bool) error\n```\n* EncodeString\n```go\nfunc (enc *gojay.Encoder) EncodeString(s string) error\n```\n\n### Structs and Maps\n\nTo encode a structure, the structure must implement the MarshalerJSONObject interface:\n```go\ntype MarshalerJSONObject interface {\n\tMarshalJSONObject(enc *gojay.Encoder)\n\tIsNil() bool\n}\n```\n`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.\n\nIsNil 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.\n\nExample of implementation for a struct:\n```go\ntype user struct {\n\tid    int\n\tname  string\n\temail string\n}\n\n// implement MarshalerJSONObject\nfunc (u *user) MarshalJSONObject(enc *gojay.Encoder) {\n\tenc.IntKey(\"id\", u.id)\n\tenc.StringKey(\"name\", u.name)\n\tenc.StringKey(\"email\", u.email)\n}\nfunc (u *user) IsNil() bool {\n\treturn u == nil\n}\n```\n\nExample of implementation for a `map[string]string`:\n```go\n// define our custom map type implementing MarshalerJSONObject\ntype message map[string]string\n\n// Implementing Marshaler\nfunc (m message) MarshalJSONObject(enc *gojay.Encoder) {\n\tfor k, v := range m {\n\t\tenc.StringKey(k, v)\n\t}\n}\n\nfunc (m message) IsNil() bool {\n\treturn m == nil\n}\n```\n\n### Arrays and Slices\nTo encode an array or a slice, the slice/array must implement the MarshalerJSONArray interface:\n```go\ntype MarshalerJSONArray interface {\n\tMarshalJSONArray(enc *gojay.Encoder)\n\tIsNil() bool\n}\n```\n`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.\n\n`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.\n\nExample of implementation:\n```go\ntype users []*user\n// implement MarshalerJSONArray\nfunc (u *users) MarshalJSONArray(enc *gojay.Encoder) {\n\tfor _, e := range u {\n\t\tenc.Object(e)\n\t}\n}\nfunc (u *users) IsNil() bool {\n\treturn len(u) == 0\n}\n```\n\n### Other types\nTo encode other types (string, int, float, booleans), you don't need to implement any interface.\n\nExample of encoding strings:\n```go\nfunc main() {\n\tname := \"Jay\"\n\tb, err := gojay.Marshal(name)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Println(string(b)) // \"Jay\"\n}\n```\n\n# Stream API\n\n### Stream Decoding\nGoJay ships with a powerful stream decoder.\n\nIt allows to read continuously from an io.Reader stream and do JIT decoding writing unmarshalled JSON to a channel to allow async consuming.\n\nWhen using the Stream API, the Decoder implements context.Context to provide graceful cancellation.\n\nTo decode a stream of JSON, you must call `gojay.Stream.DecodeStream` and pass it a `UnmarshalerStream` implementation.\n\n```go\ntype UnmarshalerStream interface {\n\tUnmarshalStream(*StreamDecoder) error\n}\n```\n\nExample of implementation of stream reading from a WebSocket connection:\n```go\n// implement UnmarshalerStream\ntype ChannelStream chan *user\n\nfunc (c ChannelStream) UnmarshalStream(dec *gojay.StreamDecoder) error {\n\tu := \u0026user{}\n\tif err := dec.Object(u); err != nil {\n\t\treturn err\n\t}\n\tc \u003c- u\n\treturn nil\n}\n\nfunc main() {\n\t// get our websocket connection\n\torigin := \"http://localhost/\"\n\turl := \"ws://localhost:12345/ws\"\n\tws, err := websocket.Dial(url, \"\", origin)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\t// create our channel which will receive our objects\n\tstreamChan := ChannelStream(make(chan *user))\n\t// borrow a decoder\n\tdec := gojay.Stream.BorrowDecoder(ws)\n\t// start decoding, it will block until a JSON message is decoded from the WebSocket\n\t// or until Done channel is closed\n\tgo dec.DecodeStream(streamChan)\n\tfor {\n\t\tselect {\n\t\tcase v := \u003c-streamChan:\n\t\t\t// Got something from my websocket!\n\t\t\tlog.Println(v)\n\t\tcase \u003c-dec.Done():\n\t\t\tlog.Println(\"finished reading from WebSocket\")\n\t\t\tos.Exit(0)\n\t\t}\n\t}\n}\n```\n\n### Stream Encoding\nGoJay ships with a powerful stream encoder part of the Stream API.\n\nIt 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.\n\nWhen using the Stream API, the Encoder implements context.Context to provide graceful cancellation.\n\nTo encode a stream of data, you must call `EncodeStream` and pass it a `MarshalerStream` implementation.\n\n```go\ntype MarshalerStream interface {\n\tMarshalStream(enc *gojay.StreamEncoder)\n}\n```\n\nExample of implementation of stream writing to a WebSocket:\n```go\n// Our structure which will be pushed to our stream\ntype user struct {\n\tid    int\n\tname  string\n\temail string\n}\n\nfunc (u *user) MarshalJSONObject(enc *gojay.Encoder) {\n\tenc.IntKey(\"id\", u.id)\n\tenc.StringKey(\"name\", u.name)\n\tenc.StringKey(\"email\", u.email)\n}\nfunc (u *user) IsNil() bool {\n\treturn u == nil\n}\n\n// Our MarshalerStream implementation\ntype StreamChan chan *user\n\nfunc (s StreamChan) MarshalStream(enc *gojay.StreamEncoder) {\n\tselect {\n\tcase \u003c-enc.Done():\n\t\treturn\n\tcase o := \u003c-s:\n\t\tenc.Object(o)\n\t}\n}\n\n// Our main function\nfunc main() {\n\t// get our websocket connection\n\torigin := \"http://localhost/\"\n\turl := \"ws://localhost:12345/ws\"\n\tws, err := websocket.Dial(url, \"\", origin)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\t// we borrow an encoder set stdout as the writer,\n\t// set the number of consumer to 10\n\t// and tell the encoder to separate each encoded element\n\t// added to the channel by a new line character\n\tenc := gojay.Stream.BorrowEncoder(ws).NConsumer(10).LineDelimited()\n\t// instantiate our MarshalerStream\n\ts := StreamChan(make(chan *user))\n\t// start the stream encoder\n\t// will block its goroutine until enc.Cancel(error) is called\n\t// or until something is written to the channel\n\tgo enc.EncodeStream(s)\n\t// write to our MarshalerStream\n\tfor i := 0; i \u003c 1000; i++ {\n\t\ts \u003c- \u0026user{i, \"username\", \"user@email.com\"}\n\t}\n\t// Wait\n\t\u003c-enc.Done()\n}\n```\n\n# Unsafe API\n\nUnsafe 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.\n\nIf you are not sure if your JSON is valid, don't use the Unsafe API.\n\nAlso, 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 :)\n\nAccess the `Unsafe` API this way:\n```go\ngojay.Unsafe.Unmarshal(b, v)\n```\n\n\n# Benchmarks\n\nBenchmarks encode and decode three different data based on size (small, medium, large).\n\nTo run benchmark for decoder:\n```bash\ncd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/decoder \u0026\u0026 make bench\n```\n\nTo run benchmark for encoder:\n```bash\ncd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder \u0026\u0026 make bench\n```\n\n# Benchmark Results\n## Decode\n\n\u003cimg src=\"https://images2.imgbox.com/78/01/49OExcPh_o.png\" width=\"500px\"\u003e\n\n### Small Payload\n[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_small_test.go)\n\n[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go)\n\n|                 | ns/op     | bytes/op     | allocs/op |\n|-----------------|-----------|--------------|-----------|\n| Std Library     | 2547      | 496          | 4         |\n| JsonIter        | 2046      | 312          | 12        |\n| JsonParser      | 1408      | 0            | 0         |\n| EasyJson        | 929       | 240          | 2         |\n| **GoJay**       | **807**   | **256**      | **2**     |\n| **GoJay-unsafe**| **712**   | **112**      | **1**     |\n\n### Medium Payload\n[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_medium_test.go)\n\n[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go)\n\n|                 | ns/op     | bytes/op | allocs/op |\n|-----------------|-----------|----------|-----------|\n| Std Library     | 30148     | 2152     | 496       |\n| JsonIter        | 16309     | 2976     | 80        |\n| JsonParser      | 7793      | 0        | 0         |\n| EasyJson        | 7957      | 232      | 6         |\n| **GoJay**       | **4984**  | **2448** | **8**     |\n| **GoJay-unsafe**| **4809**  | **144**  | **7**     |\n\n### Large Payload\n[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_large_test.go)\n\n[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go)\n\n|                 | ns/op     | bytes/op    | allocs/op |\n|-----------------|-----------|-------------|-----------|\n| JsonIter        | 210078    | 41712       | 1136      |\n| EasyJson        | 106626    | 160         | 2         |\n| JsonParser      | 66813     | 0           | 0         |\n| **GoJay**       | **52153** | **31241**   | **77**    |\n| **GoJay-unsafe**| **48277** | **2561**    | **76**    |\n\n## Encode\n\n\u003cimg src=\"https://images2.imgbox.com/e9/cc/pnM8c7Gf_o.png\" width=\"500px\"\u003e\n\n### Small Struct\n[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_small_test.go)\n\n[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go)\n\n|                | ns/op    | bytes/op     | allocs/op |\n|----------------|----------|--------------|-----------|\n| Std Library    | 1280     | 464          | 3         |\n| EasyJson       | 871      | 944          | 6         |\n| JsonIter       | 866      | 272          | 3         |\n| **GoJay**      | **543**  | **112**      | **1**     |\n| **GoJay-func** | **347**  | **0**        | **0**     |\n\n### Medium Struct\n[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_medium_test.go)\n\n[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go)\n\n|             | ns/op    | bytes/op     | allocs/op |\n|-------------|----------|--------------|-----------|\n| Std Library | 5006     | 1496         | 25        |\n| JsonIter    | 2232     | 1544         | 20        |\n| EasyJson    | 1997     | 1544         | 19        |\n| **GoJay**   | **1522** | **312**      | **14**    |\n\n### Large Struct\n[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_large_test.go)\n\n[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go)\n\n|             | ns/op     | bytes/op     | allocs/op |\n|-------------|-----------|--------------|-----------|\n| Std Library | 66441     | 20576        | 332       |\n| JsonIter    | 35247     | 20255        | 328       |\n| EasyJson    | 32053     | 15474        | 327       |\n| **GoJay**   | **27847** | **9802**     | **318**   |\n\n# Contributing\n\nContributions are welcome :)\n\nIf you encounter issues please report it in Github and/or send an email at [francois@parquet.ninja](mailto:francois@parquet.ninja)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrancoispqt%2Fgojay","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrancoispqt%2Fgojay","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrancoispqt%2Fgojay/lists"}