go-json

A fast, drop-in replacement for Go's encoding/json that skips reflection in favor of compiled opcode execution.

Library
Go
vv0.10.6
3,708stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
60/100Good
Development Activity32
Maintenance52
Community56
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
79/100Good
Architecture85
Code Quality82
Innovation78
Learning Curve70

go-json is a JSON encoder/decoder for Go built to be a fully compatible, drop-in replacement for the standard library’s encoding/json — you swap a single import path and keep every existing struct tag, Marshaler, and Unmarshaler working exactly as before. Rather than walking values with reflect at encode/decode time, go-json compiles each type into a cached sequence of opcodes keyed by the type’s runtime address, then executes that opcode program directly against unsafe.Pointer offsets, cutting out the repeated reflection overhead that dominates encoding/json’s cost on hot paths.

Beyond raw throughput, go-json adds practical extras the standard library doesn’t offer: colorized/pretty debug output, context.Context propagation into custom MarshalJSON/UnmarshalJSON implementations, a JSONPath-style query API (CreatePath) for extracting values without decoding a whole document, and type-safe field filtering via FieldQuery so callers can trim struct output without hand-rolling a second type. It also exposes streaming encode/decode and an UnorderedMap option for callers who don’t need Go’s default sorted-map-key behavior.

The project has been maintained since 2020, has shipped 78 tagged releases, and is exercised by a large internal test suite (encode_test.go and decode_test.go alone cover tens of thousands of lines) plus CI running across Linux, macOS, and Windows, three Go versions, and the race detector on every push.

What You Get

  • A drop-in replacement for encoding/json — change the import path, keep all existing json struct tags, Marshaler/Unmarshaler implementations, and Encoder/Decoder call sites
  • Opcode-compiled encoding and decoding that avoids repeated reflect calls by caching a per-type instruction sequence keyed on the type’s runtime address
  • context.Context propagation into MarshalJSON/UnmarshalJSON via MarshalContext/UnmarshalContext for request-scoped marshaling logic
  • A CreatePath JSONPath-style query API to pull specific values out of a JSON document without decoding the whole structure
  • Type-safe FieldQuery/BuildFieldQuery API to dynamically include only selected struct fields in output, avoiding a second stripped-down type
  • Colorized and indented debug output via the Debug/Color encode options for readable terminal inspection during development

Common Use Cases

  • Swapping encoding/json for go-json in high-throughput HTTP APIs or RPC services where JSON (de)serialization shows up in CPU profiles
  • Services that pass context.Context through custom marshaling logic (e.g. request-scoped tracing or auth checks fired from MarshalJSON)
  • Extracting a handful of fields from large JSON payloads via CreatePath instead of decoding and discarding most of the document
  • Trimming API response fields per-caller at encode time with FieldQuery instead of maintaining parallel DTO structs
  • Debugging JSON encoding issues locally with colorized/indented output instead of piping through a separate formatter

Under The Hood

Architecture go-json’s encode/decode paths (encode.go, decode.go, json.go) sit on top of two internal packages, internal/encoder and internal/decoder, which each implement a compiler (compiler.go) that turns a Go type into an OpcodeSet keyed by the type’s runtime pointer address and cached in a lock-free array (cachedOpcodeSets, an []atomic.Pointer[OpcodeSet]) rather than re-deriving it via reflection on every call; internal/runtime supplies the type-address bookkeeping this cache depends on, and separate vm/vm_color/vm_indent/vm_color_indent packages execute the same opcode program with different output formatting so the hot compiled path doesn’t fork per output mode. Top-level API (Marshal/Unmarshal, Encoder/Decoder, CreatePath, FieldQuery) is a thin public wrapper that acquires a pooled RuntimeContext, sets option flags (HTML escaping, UTF-8 normalization, context propagation), and delegates into these compiled paths, so the abstraction that would break the most if changed is the OpcodeSet/type-address cache — nearly everything else is built on top of it.

Tech Stack Written in pure Go (module targets go 1.19, tested through 1.21) with zero runtime dependencies outside the standard library — it deliberately reimplements pieces of encoding/json and reflect-adjacent primitives itself (internal/runtime) rather than depending on external reflection helpers. It uses unsafe.Pointer and sync/atomic directly for the type-address cache and buffer reuse instead of a general-purpose caching library, and ships a Dockerfile/docker-compose.yml so its “build on limited environment” CI job can run in a constrained container rather than the full GitHub Actions runner.

Code Quality The repo carries an unusually large test suite relative to its size — encode_test.go and decode_test.go together are tens of thousands of lines, covering type combinations exhaustively, alongside dedicated color_test.go, path_test.go, query_test.go, stream_test.go, and tagkey_test.go files. CI (.github/workflows) runs the full suite on ubuntu/macos/windows across three Go versions, once normally, once under artificial GC pressure (GOGC=1), and once with the race detector, then runs a separate golangci-lint job configured with enable-all linters (with a short, deliberate disable list). Error handling is explicit and typed (internal/errors package) rather than swallowed, and public option/query APIs are validated at call time rather than trusting caller input silently.

What Makes It Unique Most faster JSON libraries for Go (easyjson, gojay, ffjson) trade encoding/json compatibility for speed by requiring code generation or a different API; go-json’s distinguishing choice is keeping full drop-in compatibility (same interfaces, same struct tags, same Marshaler/Unmarshaler contracts) while still avoiding reflection at runtime, by compiling per-type opcode programs once and caching them by type address instead of by generated source file. Its JSONPath query support and type-safe field-filtering query system are also not offered by encoding/json or most of its faster competitors, giving it selective-read and selective-write capabilities on top of the performance work.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search