<aside> 💡
Go 1.25
JSON evolution in Go: from v1 to v2
Green Tea GC: How Go Stopped Wasting 35% of Your CPU Cycles
</aside>
The second version of the json
package coming in Go 1.25 is a big update, and it has a lot of breaking changes. The v2 package adds new features, fixes API issues and behavioral flaws, and boosts performance. Let's take a look at what's changed!
All examples are interactive, so you can read and practice at the same time.
The basic use case with Marshal
and Unmarshal
stays the same. This code works in both v1 and v2:
type Person struct {
Name string
Age int
}
alice := Person{Name: "Alice", Age: 25}
// Marshal Alice.
b, err := json.Marshal(alice)
fmt.Println(string(b), err)
// Unmarshal Alice.
err = json.Unmarshal(b, &alice)
fmt.Println(alice, err)
But the rest is pretty different. Let's go over the main changes from v1.
Write/Read • Encode/Decode • Options • Tags • Custom marshaling • Default behavior • Performance • Final thoughts
In v1, you used Encoder
to marshal to the io.Writer
, and Decoder
to unmarshal from the io.Reader
:
// Marshal Alice.
alice := Person{Name: "Alice", Age: 25}
out := new(strings.Builder) // io.Writer
enc := json.NewEncoder(out)
enc.Encode(alice)
fmt.Println(out.String())
// Unmarshal Bob.
in := strings.NewReader(`{"Name":"Bob","Age":30}`) // io.Reader
dec := json.NewDecoder(in)
var bob Person
dec.Decode(&bob)
fmt.Println(bob)
From now on, I'll leave out error handling to keep things simple.
In v2, you can use MarshalWrite
and UnmarshalRead
directly, without any intermediaries:
// Marshal Alice.
alice := Person{Name: "Alice", Age: 25}
out := new(strings.Builder)
json.MarshalWrite(out, alice)
fmt.Println(out.String())
// Unmarshal Bob.
in := strings.NewReader(`{"Name":"Bob","Age":30}`)
var bob Person
json.UnmarshalRead(in, &bob)
fmt.Println(bob)
They're not interchangeable, though:
MarshalWrite
does not add a newline, unlike the old Encoder.Encode
.UnmarshalRead
reads everything from the reader until it hits io.EOF
, while the old Decoder.Decode
only reads the next JSON value.The Encoder
and Decoder
types have been moved to the new jsontext
package, and their interfaces have changed significantly (to support low-level streaming encode/decode operations).
You can use them with json
functions to read and write JSON streams, similar to how Encode
and Decode
worked before: