Package neilotoole/jsoncolor is a drop-in replacement for stdlib
encoding/json that outputs colorized JSON.
Why? Well, jq colorizes its output by default, and color output
is desirable for many Go CLIs. This package performs colorization (and indentation) inline
in the encoder, and is significantly faster than stdlib at indentation.
From the example jc app:
Get the package per the normal mechanism (requires Go 1.17+):
go get -u github.com/neilotoole/jsoncolorThen:
package main
import (
"fmt"
"github.com/mattn/go-colorable"
json "github.com/neilotoole/jsoncolor"
"os"
)
func main() {
var enc *json.Encoder
// Note: this check will fail if running inside Goland (and
// other IDEs?) as IsColorTerminal will return false.
if json.IsColorTerminal(os.Stdout) {
// Safe to use color
out := colorable.NewColorable(os.Stdout) // needed for Windows
enc = json.NewEncoder(out)
// DefaultColors are similar to jq
clrs := json.DefaultColors()
// Change some values, just for fun
clrs.Bool = json.Color("\x1b[36m") // Change the bool color
clrs.String = json.Color{} // Disable the string color
enc.SetColors(clrs)
} else {
// Can't use color; but the encoder will still work
enc = json.NewEncoder(os.Stdout)
}
m := map[string]interface{}{
"a": 1,
"b": true,
"c": "hello",
}
if err := enc.Encode(m); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}To enable colorization, invoke enc.SetColors.
The Colors struct
holds color config. The zero value and nil are both safe for use (resulting in no colorization).
The DefaultColors func
returns a Colors struct that produces results similar to jq:
// DefaultColors returns the default Colors configuration.
// These colors largely follow jq's default colorization,
// with some deviation.
func DefaultColors() *Colors {
return &Colors{
Null: Color("\x1b[2m"),
Bool: Color("\x1b[1m"),
Number: Color("\x1b[36m"),
String: Color("\x1b[32m"),
Key: Color("\x1b[34;1m"),
Bytes: Color("\x1b[2m"),
Time: Color("\x1b[32;2m"),
Punc: Color{}, // No colorization
}
}As seen above, use the Color zero value (Color{}) to
disable colorization for that JSON element.
It can be inconvenient to use terminal codes, e.g. json.Color("\x1b[36m").
A helper package provides an adapter for fatih/color.
// import "github.com/neilotoole/jsoncolor/helper/fatihcolor"
// import "github.com/fatih/color"
// import "github.com/mattn/go-colorable"
out := colorable.NewColorable(os.Stdout) // needed for Windows
enc = json.NewEncoder(out)
fclrs := fatihcolor.DefaultColors()
// Change some values, just for fun
fclrs.Number = color.New(color.FgBlue)
fclrs.String = color.New(color.FgCyan)
clrs := fatihcolor.ToCoreColors(fclrs)
enc.SetColors(clrs)This package is a full drop-in for stdlib encoding/json
(thanks to the ancestral segmentio/encoding/json
pkg being a full drop-in).
To drop-in, just use an import alias:
import json "github.com/neilotoole/jsoncolor"See cmd/jc for a trivial CLI implementation that can accept JSON input,
and output that JSON in color.
# From project root
$ go install ./cmd/jc
$ cat ./testdata/sakila_actor.json | jcNote that this package contains golang_bench_test.go, which
is inherited from segmentj. But here we're interested in benchmark_test.go:BenchmarkEncode,
which benchmarks encoding performance versus other JSON encoder packages.
The results below benchmark the following:
- Stdlib
encoding/json(go1.17.1). segmentj:v0.1.14, which was whenjsoncolorwas forked. The newersegmentjcode performs even better.neilotoole/jsoncolor: (this package)v0.6.0.nwidger/jsoncolor:v0.3.0, latest at time of benchmarks.
Note that two other Go JSON colorization packages (hokaccha/go-prettyjson and
TylerBrock/colorjson) are excluded from
these benchmarks because they do not provide a stdlib-compatible Encoder impl.
$ go test -bench=BenchmarkEncode -benchtime="5s"
goarch: amd64
pkg: github.com/neilotoole/jsoncolor
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkEncode/stdlib_NoIndent-16 181 33047390 ns/op 8870685 B/op 120022 allocs/op
BenchmarkEncode/stdlib_Indent-16 124 48093178 ns/op 10470366 B/op 120033 allocs/op
BenchmarkEncode/segmentj_NoIndent-16 415 14658699 ns/op 3788911 B/op 10020 allocs/op
BenchmarkEncode/segmentj_Indent-16 195 30628798 ns/op 5404492 B/op 10025 allocs/op
BenchmarkEncode/neilotoole_NoIndent_NoColor-16 362 16522399 ns/op 3789034 B/op 10020 allocs/op
BenchmarkEncode/neilotoole_Indent_NoColor-16 303 20146856 ns/op 5460753 B/op 10021 allocs/op
BenchmarkEncode/neilotoole_NoIndent_Color-16 295 19989420 ns/op 10326019 B/op 10029 allocs/op
BenchmarkEncode/neilotoole_Indent_Color-16 246 24714163 ns/op 11996890 B/op 10030 allocs/op
BenchmarkEncode/nwidger_NoIndent_NoColor-16 10 541107983 ns/op 92934231 B/op 4490210 allocs/op
BenchmarkEncode/nwidger_Indent_NoColor-16 7 798088086 ns/op 117258321 B/op 6290213 allocs/op
BenchmarkEncode/nwidger_indent_NoIndent_Colo-16 10 542002051 ns/op 92935639 B/op 4490224 allocs/op
BenchmarkEncode/nwidger_indent_Indent_Color-16 7 799928353 ns/op 117259195 B/op 6290220 allocs/op
As always, take benchmarks with a large grain of salt, as they're based on a (small) synthetic benchmark.
More benchmarks would give a better picture (and note as well that the benchmarked segmentj is an older version, v0.1.14).
All that having been said, what can we surmise from these particular results?
segmentjperforms better thanstdlibat all encoding tasks.jsoncolorperforms better thansegmentjfor indentation (which makes sense, as indentation is performed inline).jsoncolorperforms better thanstdlibat all encoding tasks.
Again, trust these benchmarks at your peril. Create your own benchmarks for your own workload.
- The
.golangci.ymllinter settings have been fiddled with to hush some linting issues inherited from thesegmentiocodebase at the time of forking. Thus, the linter report may not be of great use. In an ideal world, thejsoncolorfunctionality would be ported to a more recent (and better-linted) version of thesegementiocodebase. - The
segmentioencoder (at least as ofv0.1.14) encodestime.Durationas string, whilestdliboutputs asint64. This package followsstdlib. - The
Colors.Puncfield is the fallback color for punctuation ([]{},:). As ofv0.9.0the individual classes can also be set viaColors.Brackets,Colors.Braces,Colors.Comma, andColors.Colon, each falling back toColors.Puncwhen unset. (The structural"is colored byColors.String/Colors.Key, notColors.Punc.)
Contributions are welcome! See CONTRIBUTING.md for how to build,
test, and submit changes, and please follow the Code of Conduct.
To report a security vulnerability, see SECURITY.md.
History: this package is an extract of sq's JSON encoding package, which itself is a fork of the
segmentio/encoding JSON encoding package. Note that the
original sq JSON encoder was forked from Segment's codebase at v0.1.14, so
the codebases have drifted significantly by now.
Documentation and repository housekeeping; no functional changes to the library.
- Add a package doc comment so pkg.go.dev shows a package overview.
- Add
CONTRIBUTING.md, a Contributor CovenantCODE_OF_CONDUCT.md, and GitHub issue and pull request templates. - Refresh
SECURITY.md: update supported versions and switch to private vulnerability reporting. - Restrict the CI workflows'
GITHUB_TOKENto least-privilege (contents: read) permissions. - README: add Contributing and License sections, and point the release badge at the changelog.
- #16: Add individually-configurable punctuation color fields —
Colors.Brackets,Colors.Braces,Colors.Comma, andColors.Colon— each falling back toColors.Puncwhen unset, so existing configs are unaffected. - #19: Fix nondeterministic object key order when encoding
RawMessage. Values are now re-encoded via on-the-fly tokenization, preserving source key order, instead of round-tripping through amap. - #37: Exported the
indentertype (nowIndenter) and added theNewIndenterconstructor, so external callers can construct the indenter argument accepted byAppend. TheAppendsignature now readsAppend(b []byte, x interface{}, flags AppendFlags, clrs *Colors, indentr *Indenter).
- Bumped minimum Go version from 1.17 to 1.25.
- Updated dependencies to latest:
fatih/colorv1.19.0,mattn/go-colorablev0.1.14,golang.org/x/sysv0.45.0, andgolang.org/x/termv0.43.0 (plus test-onlystretchr/testifyv1.11.1 andsegmentio/encodingv0.5.4). - Migrated the
golangci-lintconfig to the v2 format; CI now runsgolangci-lintv2.12.2 (actionv8) and the test matrix targets Go 1.25 and 1.26. - Modernized internal code to satisfy the updated linters (e.g.
reflect.Ptr→reflect.Pointer,io/ioutil→io,unsafe.Slice/unsafe.StringDatafor string/byte conversion). No behavior change.
- #38: Fix
TestCodecfailure on Go 1.22+ and update CI.- Use semantic JSON comparison in
TestCodecto handle stdlib escape sequence changes. - Bump minimum Go version from 1.16 to 1.17.
- Update CI workflows: expand test matrix to Go 1.17/1.24/1.26, fix
golangci-lintworkflow.
- Use semantic JSON comparison in
- #27: Improved Windows terminal color support checking.
- #21: Support for
encoding.TextMarshaler. - #22: Removed redundant dependencies.
- #26: Updated dependencies.
jq: sine qua non.segmentio/encoding:jsoncoloris layered into Segment's JSON encoder. They did the hard work. Much gratitude to that team.sq:jsoncoloris effectively an extract of code created specifically forsq.mattn/go-colorable: no project is complete withoutmattnhaving played a role.fatih/color: the color library.@hermannm: for several PRs.
MIT © Neil O'Toole