Go's encoding/json package supports an omitzero struct tag option for omitting fields whose values are zero. Added in Go 1.24, it gives you a clearer way to express "do not include this field when it has its zero value."
This is especially useful for types such as time.Time, integers, booleans, and custom types that define an IsZero() bool method. It also avoids some of the surprising behavior that can occur when omitempty is used for values whose zero state is not considered empty.
omitzero Syntax
type Item struct {
Count int `json:"count,omitzero"`
}
When Item is marshaled to JSON, the count field is omitted when Count is 0. When Count contains a non-zero value, the field is included normally.
omitzero tests whether a field is its zero value. If the field type provides an IsZero() bool method, encoding/json uses that method to decide whether the field should be omitted.
Omit Zero Values from JSON
The following example contains an integer and a time.Time value. Both fields are omitted because they are zero-valued.
Example:
package main
import (
"encoding/json"
"fmt"
"time"
)
type Event struct {
Name string `json:"name"`
Attempts int `json:"attempts,omitzero"`
Published time.Time `json:"published,omitzero"`
}
func main() {
event := Event{Name: "Launch"}
data, err := json.Marshal(event)
if err != nil {
panic(err)
}
fmt.Println(string(data))
}
Output:
{"name":"Launch"}
omitzero vs omitempty
Both options can remove fields from JSON output, but they answer different questions. omitempty checks whether a value is considered empty by encoding/json, while omitzero checks whether it is the type's zero value.
| Option | Main rule | Typical use |
|---|---|---|
| omitempty | Omit values considered empty | Strings, slices, maps, pointers, and other empty values |
| omitzero | Omit values considered zero | Zero-valued structs, numbers, booleans, time values, and custom IsZero rules |
A key difference appears with struct values such as time.Time. A zero time value is still a struct value, so omitempty does not necessarily express the intended rule. omitzero is designed for that case.
Include a Non-Zero Value
When the field is not zero, it remains in the encoded JSON.
Example:
package main
import (
"encoding/json"
"fmt"
)
type Job struct {
Name string `json:"name"`
Attempts int `json:"attempts,omitzero"`
}
func main() {
job := Job{
Name: "Import",
Attempts: 2,
}
data, _ := json.Marshal(job)
fmt.Println(string(data))
}
Output:
{"name":"Import","attempts":2}
Use IsZero() for Custom Types
A custom type can control its zero test by defining an IsZero() bool method. encoding/json uses that method when omitzero is present on a field of that type.
Example:
package main
import (
"encoding/json"
"fmt"
)
type Score int
func (s Score) IsZero() bool {
// Treat negative scores as unset as well.
return s <= 0
}
type Result struct {
Name string `json:"name"`
Score Score `json:"score,omitzero"`
}
func main() {
result := Result{
Name: "Asha",
Score: -1,
}
data, _ := json.Marshal(result)
fmt.Println(string(data))
}
Output:
{"name":"Asha"}
Combine omitzero and omitempty
The tag options can be combined. When both are present, the field is omitted if either rule says it should be omitted.
type Profile struct {
Nickname string `json:"nickname,omitempty,omitzero"`
}
In many cases you only need one option. Use the one that best communicates the rule your API expects.
Common Mistakes
- Assuming omitzero changes unmarshaling: it controls marshaling output; it does not change how incoming JSON is decoded.
- Using it on older Go versions: the option was introduced in Go 1.24, so older toolchains do not provide the same behavior.
- Confusing zero with empty: the two concepts overlap for some types but are not identical.
- Writing surprising IsZero methods: custom zero rules should be predictable because they directly affect serialized output.
When to Use omitzero
- Remove zero-valued timestamps from API responses.
- Omit counters, flags, or numeric fields that have not been set.
- Define domain-specific "unset" behavior with IsZero().
- Make JSON output rules more explicit than relying on omitempty.
Recommended Practices
- Use omitzero when the business rule is specifically based on a type's zero value.
- Keep custom IsZero() implementations simple and documented.
- Add JSON tests for zero and non-zero examples so API output remains stable.
- Use pointers when you must distinguish "not provided" from an explicitly supplied zero value.
Conclusion
The omitzero JSON tag gives Go programs a direct way to omit zero-valued fields during marshaling. It works naturally with built-in zero values, improves handling for struct values such as time.Time, and can be customized through IsZero(). Choose it when "zero" is the rule you want your JSON API to express.