Go 1.24's `omitzero` is another one of the best additions to the ecosystem in years
Feb. 12, 2025, 10:04 a.m.
Go 1.24's `omitzero` is another one of the best additions to the ecosystem in years
Feb. 12, 2025, 10:04 a.m.
After publishing Go 1.24's go tool
is one of the best additions to the ecosystem in years a couple of weeks ago, a few commenters pointed me to the other massive thing that was coming in the Go 1.24 release: the omitzero
JSON struct tag, and I was pretty gutted to realise I'd missed it in the release notes.
As someone who enjoys capitalising on my blog's successes, as well as Go 1.24 being released ~12 hours ago, I thought I'd write a post about omitzero
, too.
omitempty
(prior to Go 1.24)When you have a struct that's being converted to JSON, you've likely got some fields that are optional.
For instance, let's take the following struct:
type Response struct {
ID string `json:"id"`
// other fields omitted
UpdatedAt time.Time `json:"updated_at"`
}
Let's say that the UpdatedAt
should only be set after the underlying record in the database has been updated after its creation.
With this in mind, we may only want to return the field's value in JSON responses, if it has a value.
If we were to use the above struct, and marshal the data (with UpdatedAt
at its zero value):
func main() {
resp := Response_noTag{
ID: "the-id",
// no explicit initialisation of UpdatedAt
}
data, err := json.MarshalIndent(resp, "", " ")
must(err)
fmt.Printf("%s\n", data)
}
This then results in an unwanted updated_at
JSON field:
{
"id": "the-id",
"updated_at": "0001-01-01T00:00:00Z"
}
As of Go 1.23 (and prior) the best way to make updated_at
optional is to use an optional pointer and the omitempty
JSON struct tag:
type Response struct {
ID string `json:"id"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
This allows you to not specify UpdatedAt
resulting in its zero value (nil
) which will be treated as absent via the omitempty
struct tag, and therefore will not get marshalled:
{
"id": "the-id"
}
This works fine but can also be a little awkward to work with, given you now have to work with a pointer.
For those who may recognise me as the co-maintainer of oapi-codegen
, you will appreciate that how best to (un)marshal JSON in Go is something near to my heart, and over the years we've had various attempts to try and simplify this experience for our users, none of which have quite hit the mark.
omitzero
(from Go 1.24)However, from Go 1.24, we now have the omitzero
JSON struct tag which allows us to use the actual zero value of a type as an indication for it not to be marshalled.
This allows us to use the following struct definition:
type Response_omitzero struct {
ID string `json:"id"`
UpdatedAt time.Time `json:"updated_at,omitzero"`
}
Notice that we can now use a non-pointer type, which improves how we're able to interact with the type, and it marshals as expected:
{
"id": "the-id"
}
As noted, one key thing is the reduction of dealing with a lot of extra pointers. Although in this contrived example, there is only one pointer, this can get quite awkward when i.e. nesting structs with optional fields:
type Response struct {
Human *Human `json:"human"`
}
type Human struct {
DNA []byte `json:"dna"`
Name string `json:"name"`
Age *int `json:"age"`
}
Which can make it harder to initialise a struct in one go, as you're unable to get a pointer to a constant value to set the value of i.e. Age: &30
.
return Response{
Human: &Human{
Age: &30, // ๐ invalid operation: cannot take address of 30 (untyped int constant)
},
}
This can be a bit of a drag when working with code like this, and gets fairly repetitive, so if you can avoid the pointer-heavy types, that's awesome!
As noted in the proposal for omitzero
, this also has clearer semantics, given that struct{}
could be called "empty", but does not get treated as "empty" according to omitempty
.
I'm very excited for this to be a feature we can integrate into oapi-codegen
, and hope y'all are excited about it too!
If you've not had a play with Anton Zhiyanov's interactive tour of Go 1.24 yet, it has a good section on omitzero
which shows this more interactively, and allows you to edit the code snippets in the browser and re-run them.
Recent content in articles on Jamie Tanna | Software Engineer