• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

ffjson/H28-Oct-2018-323162

fflib/v1/H28-Oct-2018-7,2405,389

generator/H28-Oct-2018-581391

inception/H28-Oct-2018-2,3761,852

shared/H28-Oct-2018-5229

tests/H28-Oct-2018-6,3494,418

.gitignoreH A D28-Oct-2018157 65

.travis.ymlH A D28-Oct-2018255 1510

LICENSEH A D28-Oct-201811.1 KiB203169

MakefileH A D28-Oct-20181.5 KiB5034

NOTICEH A D28-Oct-2018215 86

README.mdH A D28-Oct-201810 KiB233151

ffjson.goH A D28-Oct-20182.4 KiB8654

README.md

1# ffjson: faster JSON for Go
2
3[![Build Status](https://travis-ci.org/pquerna/ffjson.svg?branch=master)](https://travis-ci.org/pquerna/ffjson)
4
5`ffjson` generates static `MarshalJSON` and `UnmarshalJSON` functions for structures in Go. The generated functions reduce the reliance upon runtime reflection to do serialization and are generally 2 to 3 times faster.  In cases where `ffjson` doesn't understand a Type involved, it falls back to `encoding/json`, meaning it is a safe drop in replacement.  By using `ffjson` your JSON serialization just gets faster with no additional code changes.
6
7When you change your `struct`, you will need to run `ffjson` again (or make it part of your build tools).
8
9## Blog Posts
10
11* 2014-03-31: [First Release and Background](https://journal.paul.querna.org/articles/2014/03/31/ffjson-faster-json-in-go/)
12
13## Getting Started
14
15If `myfile.go` contains the `struct` types you would like to be faster, and assuming `GOPATH` is set to a reasonable value for an existing project (meaning that in this particular example if `myfile.go` is in the `myproject` directory, the project should be under `$GOPATH/src/myproject`), you can just run:
16
17    go get -u github.com/pquerna/ffjson
18    ffjson myfile.go
19    git add myfile_ffjson.go
20
21
22## Performance Status:
23
24* `MarshalJSON` is **2x to 3x** faster than `encoding/json`.
25* `UnmarshalJSON` is **2x to 3x** faster than `encoding/json`.
26
27## Features
28
29* **Unmarshal Support:** Since v0.9, `ffjson` supports Unmarshaling of structures.
30* **Drop in Replacement:** Because `ffjson` implements the interfaces already defined by `encoding/json` the performance enhancements are transparent to users of your structures.
31* **Supports all types:** `ffjson` has native support for most of Go's types -- for any type it doesn't support with fast paths, it falls back to using `encoding/json`.  This means all structures should work out of the box. If they don't, [open a issue!](https://github.com/pquerna/ffjson/issues)
32* **ffjson: skip**: If you have a structure you want `ffjson` to ignore, add `ffjson: skip` to the doc string for this structure.
33* **Extensive Tests:** `ffjson` contains an extensive test suite including fuzz'ing against the JSON parser.
34
35
36# Using ffjson
37
38`ffjson` generates code based upon existing `struct` types.  For example, `ffjson foo.go` will by default create a new file `foo_ffjson.go` that contains serialization functions for all structs found in `foo.go`.
39
40```
41Usage of ffjson:
42
43        ffjson [options] [input_file]
44
45ffjson generates Go code for optimized JSON serialization.
46
47  -go-cmd="": Path to go command; Useful for `goapp` support.
48  -import-name="": Override import name in case it cannot be detected.
49  -nodecoder: Do not generate decoder functions
50  -noencoder: Do not generate encoder functions
51  -w="": Write generate code to this path instead of ${input}_ffjson.go.
52```
53
54Your code must be in a compilable state for `ffjson` to work. If you code doesn't compile ffjson will most likely exit with an error.
55
56## Disabling code generation for structs
57
58You might not want all your structs to have JSON code generated. To completely disable generation for a struct, add `ffjson: skip` to the struct comment. For example:
59
60```Go
61// ffjson: skip
62type Foo struct {
63   Bar string
64}
65```
66
67You can also choose not to have either the decoder or encoder generated by including `ffjson: nodecoder` or `ffjson: noencoder` in your comment. For instance, this will only generate the encoder (marshal) part for this struct:
68
69```Go
70// ffjson: nodecoder
71type Foo struct {
72   Bar string
73}
74```
75
76You can also disable encoders/decoders entirely for a file by using the `-noencoder`/`-nodecoder` commandline flags.
77
78## Using ffjson with `go generate`
79
80`ffjson` is a great fit with `go generate`. It allows you to specify the ffjson command inside your individual go files and run them all at once. This way you don't have to maintain a separate build file with the files you need to generate.
81
82Add this comment anywhere inside your go files:
83
84```Go
85//go:generate ffjson $GOFILE
86```
87
88To re-generate ffjson for all files with the tag in a folder, simply execute:
89
90```sh
91go generate
92```
93
94To generate for the current package and all sub-packages, use:
95
96```sh
97go generate ./...
98```
99This is most of what you need to know about go generate, but you can sese more about [go generate on the golang blog](http://blog.golang.org/generate).
100
101## Should I include ffjson files in VCS?
102
103That question is really up to you. If you don't, you will have a more complex build process. If you do, you have to keep the generated files updated if you change the content of your structs.
104
105That said, ffjson operates deterministically, so it will generate the same code every time it run, so unless your code changes, the generated content should not change. Note however that this is only true if you are using the same ffjson version, so if you have several people working on a project, you might need to synchronize your ffjson version.
106
107## Performance pitfalls
108
109`ffjson` has a few cases where it will fall back to using the runtime encoder/decoder. Notable cases are:
110
111* Interface struct members. Since it isn't possible to know the type of these types before runtime, ffjson has to use the reflect based coder.
112* Structs with custom marshal/unmarshal.
113* Map with a complex value. Simple types like `map[string]int` is fine though.
114* Inline struct definitions `type A struct{B struct{ X int} }` are handled by the encoder, but currently has fallback in the decoder.
115* Slices of slices / slices of maps are currently falling back when generating the decoder.
116
117## Reducing Garbage Collection
118
119`ffjson` already does a lot to help garbage generation. However whenever you go through the json.Marshal you get a new byte slice back. On very high throughput servers this can lead to increased GC pressure.
120
121### Tip 1: Use ffjson.Marshal() / ffjson.Unmarshal()
122
123This is probably the easiest optimization for you. Instead of going through encoding/json, you can call ffjson. This will disable the checks that encoding/json does to the json when it receives it from struct functions.
124
125```Go
126	import "github.com/pquerna/ffjson/ffjson"
127
128	// BEFORE:
129	buf, err := json.Marshal(&item)
130
131	// AFTER:
132	buf, err := ffjson.Marshal(&item)
133```
134This simple change is likely to double the speed of your encoding/decoding.
135
136
137[![GoDoc][1]][2]
138[1]: https://godoc.org/github.com/pquerna/ffjson/ffjson?status.svg
139[2]: https://godoc.org/github.com/pquerna/ffjson/ffjson#Marshal
140
141### Tip 2: Pooling the buffer
142
143On servers where you have a lot of concurrent encoding going on, you can hand back the byte buffer you get from json.Marshal once you are done using it. An example could look like this:
144```Go
145import "github.com/pquerna/ffjson/ffjson"
146
147func Encode(item interface{}, out io.Writer) {
148	// Encode
149	buf, err := ffjson.Marshal(&item)
150
151	// Write the buffer
152	_,_ = out.Write(buf)
153
154	// We are now no longer need the buffer so we pool it.
155	ffjson.Pool(buf)
156}
157```
158Note that the buffers you put back in the pool can still be reclaimed by the garbage collector, so you wont risk your program building up a big memory use by pooling the buffers.
159
160[![GoDoc][1]][2]
161[1]: https://godoc.org/github.com/pquerna/ffjson/ffjson?status.svg
162[2]: https://godoc.org/github.com/pquerna/ffjson/ffjson#Pool
163
164### Tip 3: Creating an Encoder
165
166There might be cases where you need to encode many objects at once. This could be a server backing up, writing a lot of entries to files, etc.
167
168To do this, there is an interface similar to `encoding/json`, that allow you to create a re-usable encoder. Here is an example where we want to encode an array of the `Item` type, with a comma between entries:
169```Go
170import "github.com/pquerna/ffjson/ffjson"
171
172func EncodeItems(items []Item, out io.Writer) {
173        // We create an encoder.
174	enc := ffjson.NewEncoder(out)
175
176	for i, item := range items {
177		// Encode into the buffer
178		err := enc.Encode(&item)
179
180		// If err is nil, the content is written to out, so we can write to it as well.
181		if i != len(items) -1 {
182			_,_ = out.Write([]byte{','})
183		}
184	}
185}
186```
187
188
189Documentation: [![GoDoc][1]][2]
190[1]: https://godoc.org/github.com/pquerna/ffjson/ffjson?status.svg
191[2]: https://godoc.org/github.com/pquerna/ffjson/ffjson#Encoder
192
193## Tip 4: Avoid interfaces
194
195We don't want to dictate how you structure your data, but having interfaces in your code will make ffjson use the golang encoder for these. When ffjson has to do this, it may even become slower than using `json.Marshal` directly.
196
197To see where that happens, search the generated `_ffjson.go` file for the text `Falling back`, which will indicate where ffjson is unable to generate code for your data structure.
198
199## Tip 5: `ffjson` all the things!
200
201You should not only create ffjson code for your main struct, but also any structs that is included/used in your json code.
202
203So if your struct looks like this:
204```Go
205type Foo struct {
206  V Bar
207}
208```
209You should also make sure that code is generated for `Bar` if it is placed in another file. Also note that currently it requires you to do this in order, since generating code for `Foo` will check if code for `Bar` exists. This is only an issue if `Foo` and `Bar` are placed in different files. We are currently working on allowing simultaneous generation of an entire package.
210
211
212## Improvements, bugs, adding features, and taking ffjson new directions!
213
214Please [open issues in Github](https://github.com/pquerna/ffjson/issues) for ideas, bugs, and general thoughts.  Pull requests are of course preferred :)
215
216## Similar projects
217
218* [go-codec](https://github.com/ugorji/go/tree/master/codec#readme). Very good project, that also allows streaming en/decoding, but requires you to call the library to use.
219* [megajson](https://github.com/benbjohnson/megajson). This has limited support, and development seems to have almost stopped at the time of writing.
220
221# Credits
222
223`ffjson` has recieved significant contributions from:
224
225* [Klaus Post](https://github.com/klauspost)
226* [Paul Querna](https://github.com/pquerna)
227* [Erik Dubbelboer](https://github.com/erikdubbelboer)
228
229## License
230
231`ffjson` is licensed under the [Apache License, Version 2.0](./LICENSE)
232
233