Go 1.27 stabilizes new encoding/json/v2 API with stricter RFC 8259 compliance
Go 1.27 introduces stable encoding/json/v2 and encoding/json/jsontext packages that fix long-standing behavioral flaws, API restrictions, and performance limitations in the original encoding/json package.
Summary
The Go team has declared the encoding/json/v2 and encoding/json/jsontext packages stable and generally available as part of Go 1.27, following a year of experimental development and community feedback. The new packages address numerous accumulated problems in the original encoding/json implementation, including security-relevant issues like accepting invalid UTF-8 and duplicate object member names, both of which violate RFC 8259. The v2 API also resolves practical developer pain points such as the inability to pass options through Marshal and Unmarshal functions, trailing junk rejection when unmarshaling from io.Reader, and inconsistent calling of pointer-receiver MarshalJSON methods.
The redesign introduces a lower-level encoding/json/jsontext package for token-level streaming, which enables more efficient processing and serves as the foundation for the higher-level v2 API. Key behavioral changes include nil slices and maps marshaling to empty JSON arrays and objects rather than null, and case-sensitive struct field matching during unmarshaling. These changes deliberately break compatibility with v1, which the team determined was impossible to fix within the Go 1 compatibility promise.
What changes in encoding/json/v2
- RFC 8259 compliance and security hardening: The v2 decoder rejects invalid UTF-8 and duplicate object member names by default, closing a known attack vector related to CVE-2017-12635 and eliminating silent data corruption from ambiguous inputs.
- Nil slice and map marshaling: Nil slices and maps now encode as empty JSON arrays (
[]) and objects ({}) rather thannull, improving interoperability with strict JSON implementations that cannot unmarshal null into array or object types. - Case-sensitive unmarshaling: JSON object member names are now matched to Go struct fields case-sensitively, removing a surprising default that posed security risks and performance penalties.
- API option plumbing: Options can now be passed through Marshal and Unmarshal functions and propagate through custom Marshaler and Unmarshaler implementations, fixing the v1 limitation where Decoder.DisallowUnknownFields lost effect inside custom unmarshal methods.
- Streaming token API: The encoding/json/jsontext package provides allocation-efficient token reading and writing, replacing the allocation-heavy Decoder.Token method and enabling true streaming without buffering entire JSON values in memory.
Why it matters
For Go developers maintaining network services, APIs, and cloud infrastructure, the v2 package eliminates an entire class of interoperability bugs and security vulnerabilities that have persisted for over a decade. Linux distribution packagers and container image maintainers can expect downstream Go applications to gradually migrate toward stricter, more predictable JSON handling, though the v1 package remains available for backward compatibility. The performance improvements from streaming token processing and eliminated quadratic behavior in recursive marshal/unmarshal calls will particularly benefit high-throughput services and tools in the cloud-native ecosystem.
What to do
- Update to Go 1.27 to access the stable encoding/json/v2 and encoding/json/jsontext packages.
Original reference: go.dev →