1[](https://github.com/neilotoole/jsoncolor/actions?query=workflow%3AGo)
2[](https://goreportcard.com/report/neilotoole/jsoncolor)
3[](https://github.com/neilotoole/jsoncolor/releases/tag/v0.7.0)
4[](https://pkg.go.dev/github.com/neilotoole/jsoncolor)
5[](./LICENSE)
6
7# jsoncolor
8
9Package `neilotoole/jsoncolor` is a drop-in replacement for stdlib
10[`encoding/json`](https://pkg.go.dev/encoding/json) that outputs colorized JSON.
11
12Why? Well, [`jq`](https://jqlang.github.io/jq/) colorizes its output by default, and color output
13is desirable for many Go CLIs. This package performs colorization (and indentation) inline
14in the encoder, and is significantly faster than stdlib at indentation.
15
16From the example [`jc`](./cmd/jc/main.go) app:
17
18
19
20## Usage
21
22Get the package per the normal mechanism (requires Go 1.16+):
23
24```shell
25go get -u github.com/neilotoole/jsoncolor
26```
27
28Then:
29
30```go
31package main
32
33import (
34 "fmt"
35 "github.com/mattn/go-colorable"
36 json "github.com/neilotoole/jsoncolor"
37 "os"
38)
39
40func main() {
41 var enc *json.Encoder
42
43 // Note: this check will fail if running inside Goland (and
44 // other IDEs?) as IsColorTerminal will return false.
45 if json.IsColorTerminal(os.Stdout) {
46 // Safe to use color
47 out := colorable.NewColorable(os.Stdout) // needed for Windows
48 enc = json.NewEncoder(out)
49
50 // DefaultColors are similar to jq
51 clrs := json.DefaultColors()
52
53 // Change some values, just for fun
54 clrs.Bool = json.Color("\x1b[36m") // Change the bool color
55 clrs.String = json.Color{} // Disable the string color
56
57 enc.SetColors(clrs)
58 } else {
59 // Can't use color; but the encoder will still work
60 enc = json.NewEncoder(os.Stdout)
61 }
62
63 m := map[string]interface{}{
64 "a": 1,
65 "b": true,
66 "c": "hello",
67 }
68
69 if err := enc.Encode(m); err != nil {
70 fmt.Fprintln(os.Stderr, err)
71 os.Exit(1)
72 }
73}
74```
75
76### Configuration
77
78To enable colorization, invoke [`enc.SetColors`](https://pkg.go.dev/github.com/neilotoole/jsoncolor#Encoder.SetColors).
79
80The [`Colors`](https://pkg.go.dev/github.com/neilotoole/jsoncolor#Colors) struct
81holds color config. The zero value and `nil` are both safe for use (resulting in no colorization).
82
83The [`DefaultColors`](https://pkg.go.dev/github.com/neilotoole/jsoncolor#DefaultColors) func
84returns a `Colors` struct that produces results similar to `jq`:
85
86```go
87// DefaultColors returns the default Colors configuration.
88// These colors largely follow jq's default colorization,
89// with some deviation.
90func DefaultColors() *Colors {
91 return &Colors{
92 Null: Color("\x1b[2m"),
93 Bool: Color("\x1b[1m"),
94 Number: Color("\x1b[36m"),
95 String: Color("\x1b[32m"),
96 Key: Color("\x1b[34;1m"),
97 Bytes: Color("\x1b[2m"),
98 Time: Color("\x1b[32;2m"),
99 Punc: Color{}, // No colorization
100 }
101}
102```
103
104As seen above, use the `Color` zero value (`Color{}`) to
105disable colorization for that JSON element.
106
107### Helper for `fatih/color`
108
109It can be inconvenient to use terminal codes, e.g. `json.Color("\x1b[36m")`.
110A helper package provides an adapter for [`fatih/color`](https://github.com/fatih/color).
111
112```go
113 // import "github.com/neilotoole/jsoncolor/helper/fatihcolor"
114 // import "github.com/fatih/color"
115 // import "github.com/mattn/go-colorable"
116
117 out := colorable.NewColorable(os.Stdout) // needed for Windows
118 enc = json.NewEncoder(out)
119
120 fclrs := fatihcolor.DefaultColors()
121 // Change some values, just for fun
122 fclrs.Number = color.New(color.FgBlue)
123 fclrs.String = color.New(color.FgCyan)
124
125 clrs := fatihcolor.ToCoreColors(fclrs)
126 enc.SetColors(clrs)
127```
128
129### Drop-in for `encoding/json`
130
131This package is a full drop-in for stdlib [`encoding/json`](https://pkg.go.dev/encoding/json)
132(thanks to the ancestral [`segmentio/encoding/json`](https://pkg.go.dev/github.com/segmentio/encoding/json)
133pkg being a full drop-in).
134
135To drop-in, just use an import alias:
136
137```go
138 import json "github.com/neilotoole/jsoncolor"
139```
140
141## Example app: `jc`
142
143See [`cmd/jc`](cmd/jc/main.go) for a trivial CLI implementation that can accept JSON input,
144and output that JSON in color.
145
146```shell
147# From project root
148$ go install ./cmd/jc
149$ cat ./testdata/sakila_actor.json | jc
150```
151
152## Benchmarks
153
154Note that this package contains [`golang_bench_test.go`](./golang_bench_test.go), which
155is inherited from `segmentj`. But here we're interested in [`benchmark_test.go:BenchmarkEncode`](./benchmark_test.go),
156which benchmarks encoding performance versus other JSON encoder packages.
157The results below benchmark the following:
158
159- Stdlib [`encoding/json`](https://pkg.go.dev/encoding/json) (`go1.17.1`).
160- [`segmentj`](https://github.com/segmentio/encoding): `v0.1.14`, which was when `jsoncolor` was forked. The newer `segmentj` code performs even better.
161- `neilotoole/jsoncolor`: (this package) `v0.6.0`.
162- [`nwidger/jsoncolor`](https://github.com/nwidger/jsoncolor): `v0.3.0`, latest at time of benchmarks.
163
164Note that two other Go JSON colorization packages ([`hokaccha/go-prettyjson`](https://github.com/hokaccha/go-prettyjson) and
165[`TylerBrock/colorjson`](https://github.com/TylerBrock/colorjson)) are excluded from
166these benchmarks because they do not provide a stdlib-compatible `Encoder` impl.
167
168```
169$ go test -bench=BenchmarkEncode -benchtime="5s"
170goarch: amd64
171pkg: github.com/neilotoole/jsoncolor
172cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
173BenchmarkEncode/stdlib_NoIndent-16 181 33047390 ns/op 8870685 B/op 120022 allocs/op
174BenchmarkEncode/stdlib_Indent-16 124 48093178 ns/op 10470366 B/op 120033 allocs/op
175BenchmarkEncode/segmentj_NoIndent-16 415 14658699 ns/op 3788911 B/op 10020 allocs/op
176BenchmarkEncode/segmentj_Indent-16 195 30628798 ns/op 5404492 B/op 10025 allocs/op
177BenchmarkEncode/neilotoole_NoIndent_NoColor-16 362 16522399 ns/op 3789034 B/op 10020 allocs/op
178BenchmarkEncode/neilotoole_Indent_NoColor-16 303 20146856 ns/op 5460753 B/op 10021 allocs/op
179BenchmarkEncode/neilotoole_NoIndent_Color-16 295 19989420 ns/op 10326019 B/op 10029 allocs/op
180BenchmarkEncode/neilotoole_Indent_Color-16 246 24714163 ns/op 11996890 B/op 10030 allocs/op
181BenchmarkEncode/nwidger_NoIndent_NoColor-16 10 541107983 ns/op 92934231 B/op 4490210 allocs/op
182BenchmarkEncode/nwidger_Indent_NoColor-16 7 798088086 ns/op 117258321 B/op 6290213 allocs/op
183BenchmarkEncode/nwidger_indent_NoIndent_Colo-16 10 542002051 ns/op 92935639 B/op 4490224 allocs/op
184BenchmarkEncode/nwidger_indent_Indent_Color-16 7 799928353 ns/op 117259195 B/op 6290220 allocs/op
185```
186
187As always, take benchmarks with a large grain of salt, as they're based on a (small) synthetic benchmark.
188More benchmarks would give a better picture (and note as well that the benchmarked `segmentj` is an older version, `v0.1.14`).
189
190All that having been said, what can we surmise from these particular results?
191
192- `segmentj` performs better than `stdlib` at all encoding tasks.
193- `jsoncolor` performs better than `segmentj` for indentation (which makes sense, as indentation is performed inline).
194- `jsoncolor` performs better than `stdlib` at all encoding tasks.
195
196Again, trust these benchmarks at your peril. Create your own benchmarks for your own workload.
197
198## Notes
199
200- The [`.golangci.yml`](./.golangci.yml) linter settings have been fiddled with to hush some
201 linting issues inherited from the `segmentio` codebase at the time of forking. Thus, the linter report
202 may not be of great use. In an ideal world, the `jsoncolor` functionality would be [ported](https://github.com/neilotoole/jsoncolor/issues/15) to a
203 more recent (and better-linted) version of the `segementio` codebase.
204- The `segmentio` encoder (at least as of `v0.1.14`) encodes `time.Duration` as string, while `stdlib` outputs as `int64`.
205 This package follows `stdlib`.
206- The [`Colors.Punc`](https://pkg.go.dev/github.com/neilotoole/jsoncolor#Colors) field controls all
207 punctuation colorization, i.e. `[]{},:"`. It is probably worthwhile to [separate](https://github.com/neilotoole/jsoncolor/issues/16)
208 these out into individually-configurable elements.
209
210<a name="history"></a>
211## CHANGELOG
212
213History: this package is an extract of [`sq`](https://github.com/neilotoole/sq)'s JSON encoding package, which itself is a fork of the
214[`segmentio/encoding`](https://github.com/segmentio/encoding) JSON encoding package. Note that the
215original `sq` JSON encoder was forked from Segment's codebase at `v0.1.14`, so
216the codebases have drifted significantly by now.
217
218### [v0.7.1](https://github.com/neilotoole/jsoncolor/releases/tag/v0.7.1)
219
220- [#27](https://github.com/neilotoole/jsoncolor/pull/27): Improved Windows terminal color support checking.
221
222### [v0.7.0](https://github.com/neilotoole/jsoncolor/releases/tag/v0.7.0)
223
224- [#21](https://github.com/neilotoole/jsoncolor/pull/21): Support for [`encoding.TextMarshaler`](https://pkg.go.dev/encoding#TextMarshaler).
225- [#22](https://github.com/neilotoole/jsoncolor/pull/22): Removed redundant dependencies.
226- [#26](https://github.com/neilotoole/jsoncolor/pull/26): Updated dependencies.
227
228## Acknowledgments
229
230- [`jq`](https://stedolan.github.io/jq/): sine qua non.
231- [`segmentio/encoding`](https://github.com/segmentio/encoding): `jsoncolor` is layered into Segment's JSON encoder. They did the hard work. Much gratitude to that team.
232- [`sq`](https://github.com/neilotoole/sq): `jsoncolor` is effectively an extract of code created specifically for `sq`.
233- [`mattn/go-colorable`](https://github.com/mattn/go-colorable): no project is complete without `mattn` having played a role.
234- [`fatih/color`](https://github.com/fatih/color): the color library.
235- [`@hermannm`](https://github.com/hermannm): for several PRs.
236
237### Related
238
239- [`nwidger/jsoncolor`](https://github.com/nwidger/jsoncolor)
240- [`hokaccha/go-prettyjson`](https://github.com/hokaccha/go-prettyjson)
241- [`TylerBrock/colorjson`](https://github.com/TylerBrock/colorjson)