Releases: buger/jsonparser
v1.6.0 — Append function + zero open known issues
🔒 Covered by ReqProof — L3 Assurance (123 requirements, 0 errors, 0 warnings, 0 open known issues)
New API: Append
// Append to an array without knowing its length
data, _ = jsonparser.Append(data, []byte(`"new_item"`), "items")Append(data, value, keys...) ([]byte, error) — clean array-append API. Works on top-level and nested arrays. Auto-creates missing paths as single-element arrays. No need for [N] path syntax.
Bug fixes — all known issues resolved
| KI | Fix |
|---|---|
| KI-2 | ParseInt("-") now returns MalformedValueError (was returning 0, nil) |
| KI-3 | Disposition corrected to fixed (auto-coerce was implemented in v1.3.0) |
| KI-4 | Set([1,2,3], val, "[5]") now appends instead of returning KeyPathNotFoundError |
Zero open known issues. All 4 KIs are now status: fixed.
Full changelog: CHANGELOG.md
v1.5.1 — 6.1x large-payload speedup + fresh benchmarks
🔒 Covered by ReqProof — L3 Assurance (123 requirements, 0 errors, 0 warnings)
Performance — 6.1x large-payload speedup
Two optimizations found via ADHD-driven profiling + 10 parallel worktree experiments:
-
Fix stringEnd unbounded backslash scan —
stringEndConfigwas scanning the ENTIRE remaining parent document for backslashes instead of just the string body. Bounded todata[:firstQuote]. 128µs → 22µs (5.8x). -
SWAR string scan — replaced two separate
bytes.IndexBytecalls with a single inline 8-byte SWAR loop checking for both"and\simultaneously. 22µs → 21µs (additional 8%).
Fresh benchmarks (all libraries at latest versions)
Methodology: Apple M4 Max (ARM64), Go 1.26.3, median of 5 runs. All comparison libraries updated. The
encoding/jsonbenchmark no longer uses ffjson-generated methods (fixed #126 in v1.3.1).
| Payload | jsonparser | encoding/json | easyjson | jsonparser advantage |
|---|---|---|---|---|
| Small (190B) | 382 ns / 0 allocs | 1,335 ns / 9 allocs | 312 ns / 4 allocs | 3.5x faster, 0 allocs |
| Medium (2.4kB) | 3,894 ns / 0 allocs | 10,564 ns / 18 allocs | 2,444 ns / 7 allocs | 2.7x faster, 0 allocs |
| Large (24kB) | 20,788 ns / 0 allocs | 134,123 ns / 147 allocs | 32,765 ns / 134 allocs | 6.4x faster, 0 allocs |
jsonparser is the fastest library overall on large payloads and the only zero-allocation parser.
Full changelog: CHANGELOG.md
v1.5.0 — Config (lenient parsing), streaming ReaderParser, name aliases
🔒 Covered by ReqProof — L3 Assurance (123 requirements, 0 errors, 0 warnings)
Config struct — opt-in lenient parsing (#160, #115)
var Lenient = jsonparser.Config{AllowSingleQuotes: true, AllowUnknownEscapes: true}
Lenient.Get(data, "key") // parses {'key':'value'} and unknown escapesSingle-quote support and lenient escape handling via an opt-in Config. Default stays strict (RFC 8259).
Streaming ReaderParser (#132, #257)
rp := jsonparser.NewReaderParser(file) // any io.Reader
rp.Get("users", "[0]", "name") // path-based access from a streamPath-based JSON access from an io.Reader — parse 10GB+ files without loading into memory. Buffers in 64KB chunks; memory bounded by the largest value.
Name aliases (#66)
EachArray, EachObject, EachArrayErr, EachArrayWildcard — canonical EachXxx pattern. Old XxxEach names kept for backward compatibility.
Proof coverage
- 2 new SYS-REQs (115: Config/lenient, 116: streaming)
- 123 requirements, 0 errors, 0 warnings, 279/279 functions traced
Full changelog: CHANGELOG.md
v1.4.0 — 9 new APIs, 20 issues resolved
New APIs (all backward-compatible)
Iteration with error/break control
ArrayEachErr— like ArrayEach but the callback returnserror. Returnio.EOFfor graceful stop, any other error to abort. Resolves #53, #129, #176, #230, #255, #262.EachKeyErr— same pattern for EachKey.
Safe string handling
Escape(s string) []byte— RFC 8259 string escaping (inverse ofUnescape). Produces a quoted JSON string literal.SetString(data, val, keys...)—Setwith auto-quoted value. No more invalid JSON from forgetting quotes. Resolves #144, #158, #218, #270.
Container accessors
GetArrayLen(data, keys...) (int, error)— count array elements without a callback. Resolves #175, #261.GetObjectLen(data, keys...) (int, error)— count object key-value pairs.GetUint64(data, keys...) (uint64, error)— uint64 variant ofGetInt. Resolves #271.
Delete found signal
DeleteFound(data, keys...) ([]byte, bool)— returns whether the key was found. Resolves #229.
Wildcard paths
EachKeyWildcard,ArrayEachWildcard,SetWildcard—[*]path component to iterate/set all elements. Resolves #112.jsonparser.SetWildcard(data, []byte("true"), "users", "[*]", "active")
JSONPath compiled paths
ParsePath("$.users[0].name")→[]string{"users", "[0]", "name"}CompilePath+CompiledPath— pre-compile and reuse with Get/Set/Delete/etc. Resolves #234, #251.path, _ := jsonparser.CompilePath("$.person.name.fullName") name, _ := path.Get(data) // reuse across calls
Fixes
- EachKey no longer panics with >64 key components (#56)
- Set pre-allocates output buffer, reducing allocations from 6 to 1 (#107)
Proof coverage
- 3 new SYS-REQs (112: container length, 113: wildcard paths, 114: compiled paths)
- 121 total requirements, 0 errors, 0 warnings, 384 MC/DC witness rows (0 uncovered)
- All new APIs traced via source-native annotations
Acknowledgments
Implemented by codex (gpt-5-codex) via codex exec. Proof coverage by ReqProof.
Full changelog: CHANGELOG.md
v1.3.1 — Bug fixes + proof strengthening
Bug fixes
-
Fix Set/Delete input-buffer aliasing (#209, #141) —
SetandDeleteno longer corrupt the caller's input[]bytewhen the slice has spare capacity. Theappend()call path was writing into the backing array beyond the returned slice. Now all mutation paths allocate a fresh buffer. -
Fix EachKey array-index inconsistency (#232) —
EachKeynow descends into terminal array-index paths (e.g."key", "[0]") consistently withGet. PreviouslyEachKeyreturned empty whereGetsucceeded on the same path. -
Fix benchmark measuring ffjson, not encoding/json (#126) — the benchmark payload types had ffjson-generated
MarshalJSON/UnmarshalJSONmethods, so the "10x faster than encoding/json" comparison was silently measuring ffjson. Now uses plain types with no generated methods.
Proof strengthening (how these bugs escaped, and the gates that prevent recurrence)
| Bug | Proof gap | New gate |
|---|---|---|
| #209/#141 Set aliasing | No obligation said "Set must not mutate the input buffer" | New obligation no_input_mutation + assertInputUnchanged gate (snapshots input + backing-array capacity before every Set/Delete, verifies unchanged after) |
| #232 EachKey ≠ Get | No obligation said "EachKey must resolve paths identically to Get" | New obligation api_consistency + TestApiConsistencyEachKeyMatchesGet gate (random JSON + paths, asserts EachKey result == Get result) |
| #126 benchmark ffjson | Proof didn't cover the benchmark suite | Benchmark honesty lint: verifies no benchmark type implements json.Marshaler/json.Unmarshaler |
Contributed by codex (gpt-5-codex) via codex exec.
v1.3.0 — Formally verified by ReqProof (L3 Assurance)
🔒 Formally verified by ReqProof
jsonparser v1.3.0 is the first Go library proven to L3 assurance by ReqProof — a git-native requirements-engineering and formal-verification platform. Every public API is traced to a formal requirement, every requirement is tested with 100% MC/DC coverage, and the entire parser is fuzzed by a custom structure-aware JSON fuzzer at 250k inputs/sec.
The proof review caught 7 real bugs that years of community use, OSS-Fuzz, and standard fuzzing had missed.
Read the root-cause analysis →
Security / bug fixes
- Fix Delete panic on malformed input with leading comma (OSS-Fuzz 4649128545288192)
- Fix empty-string key-component panics (8 sites) — reported by @c-tonneslan (#284)
- Fix Set data loss on scalar arrays (#267) — reported by @Solaris-star (#286)
- Fix Set malformed-JSON output on cross-type paths (auto-coerce)
- Fix Delete trailing-comma malformation
- Fix ArrayEach spurious callback on non-array root
- Fix lone-Unicode-surrogate mishandling in Unescape (now matches encoding/json)
Performance
- parseInt fast-path — 22–37% faster on short integers — @trevorprater (#285)
- stringEnd SIMD fast path — 12× faster on no-escape strings
Full changelog: CHANGELOG.md
Acknowledgments
Thank you to @c-tonneslan, @Solaris-star, @trevorprater, and OSS-Fuzz for the reports and contributions.
v1.2.0
v1.1.2
What's Changed
- Updated travis to build for 1.13 to 1.15 by @janreggie in #225
- fix issue #150 (in deleting case) by @daria-kay in #226
- fixing the oss-fuzz issue by @daria-kay in #227
- Fix parseInt overflow check false negative by @carsonip in #231
- Added bespoke error for null cases by @jonomacd in #228
- Fuzzing: Add CIFuzz by @AdamKorcz in #239
- Added latest versions of go to tests by @moredure in #244
- fix EachKey pIdxFlags allocation by @unxcepted in #241
- fix: prevent panic on negative slice index in Delete with malformed JSON (GO-2026-4514) by @dbarrosop in #276
New Contributors
- @janreggie made their first contribution in #225
- @Villenny made their first contribution in #223
- @daria-kay made their first contribution in #226
- @carsonip made their first contribution in #231
- @jonomacd made their first contribution in #228
- @moredure made their first contribution in #244
- @unxcepted made their first contribution in #241
- @dbarrosop made their first contribution in #276
Full Changelog: v1.1.1...v1.1.2
v1.1.1
v1.1.0
Christmas present 🎁
- Improve
Setperformance by 20% #196 - Memory allocation improvements during Array iteration #208
- Add support for
ppc64-learchitecture #213 - Fixed repeated paths from incorrectly incrementing data offset #215
- Fixed array iteration when you have more than 64 items #216
- Fix possible memory confusion in unsafe slice cast #204
- Fixed usage of Array iteration with
Set#199 - Integration with go-fuzz #217
Thanks to everyone who participated in the project!
@AllenX2018 @saginadir @xsandr @nagesh4193 @rrgilchrist @floren @AdamKorcz @jlauinger