Golang json omitempty: meaning, zero values, and nested structs

Golang json omitempty and omitempty meaning in encoding/json; golang omitempty with strings, numbers, pointers; why omitempty has no effect omitting nested struct value fields; use pointers for optional objects; go json omitempty pitfalls.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

Golang json omitempty: meaning, zero values, and nested structs

Searches for golang json omitempty, golang omitempty, go json omitempty, json omitempty, omitempty golang, omitempty json golang, go omitempty, omitempty, and omitempty meaning all point at the same encoding/json rule: omitempty skips a field when its Go value is “empty” under the encoder’s definition. A frequent surprise—captured by queries like omitempty has no effect on nested struct fields—is that a non-pointer nested struct is still a real value: it marshals as {}, so the outer key is usually not omitted. This guide spells out omitempty behavior, that nested-struct pitfall, and safer patterns. For broader JSON workflows, see parse JSON in Go and struct tags.

Tested with Go 1.24 on Linux.


What omitempty does

In a struct tag such as `json:"name,omitempty"`, the word omitempty is handled by encoding/json. When marshaling, if the field’s value counts as empty, the field is left out of the JSON object. When unmarshaling, omitted keys simply leave the field at its zero value.


Simple fields: strings, numbers, and booleans

Zero values—"" for string, 0 for numeric types, false for bool—are empty for omitempty, so they disappear from output:

go
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name string `json:"name,omitempty"`
	Age  int    `json:"age,omitempty"`
}

func main() {
	b, _ := json.Marshal(&Person{Name: ""})
	fmt.Println(string(b))
}
Output

Running the program prints {}, because both fields are at their zero values.


Slices, maps, and pointers

nil or empty slices and maps count as empty. A nil pointer is also empty, which is the usual way to represent “this object was not set”:

go
package main

import (
	"encoding/json"
	"fmt"
)

type Order struct {
	Items []string          `json:"items,omitempty"`
	Meta  map[string]string `json:"meta,omitempty"`
}

func main() {
	b, _ := json.Marshal(&Order{})
	fmt.Println(string(b))
}
Output

Output is {}.


Nested structs: value vs pointer (the common gotcha)

Non-pointer nested struct

A nested value struct is never nil. Its zero value is “a struct whose fields are zero,” which still encodes as a JSON object. omitempty on the outer field therefore does not remove the key—you often see {"address":{}}, which matches searches claiming omitempty has no effect on nested struct fields (for dropping the whole block):

go
package main

import (
	"encoding/json"
	"fmt"
)

type Address struct {
	City  string `json:"city,omitempty"`
	State string `json:"state,omitempty"`
}

type User struct {
	Name    string  `json:"name,omitempty"`
	Address Address `json:"address,omitempty"` // value, not pointer
}

func main() {
	b, _ := json.Marshal(&User{})
	fmt.Println(string(b))
}
Output
text
{"address":{}}

Inner omitempty tags still omit zero inner fields, but the address key remains because the struct value is not considered empty at the outer level.

Pointer to nested struct

Use a pointer when the whole nested object is optional. A nil pointer is empty, so the key disappears:

go
package main

import (
	"encoding/json"
	"fmt"
)

type Address struct {
	City string `json:"city,omitempty"`
}

type User struct {
	Name    string   `json:"name,omitempty"`
	Address *Address `json:"address,omitempty"`
}

func main() {
	b, _ := json.Marshal(&User{})
	fmt.Println(string(b))
}
Output
text
{}

That pattern is the standard fix when you truly need golang json omitempty semantics for optional nested documents.


Booleans and meaningful zeros

omitempty on a bool omits false, which is wrong when clients must distinguish “false” from “field not sent”. Use a pointer *bool, drop omitempty, or marshal with a custom type.


Quick test with the standard library

You do not need external assertion libraries to sanity-check output:

go
package main

import (
	"encoding/json"
	"fmt"
	"strings"
)

func main() {
	type sample struct {
		Field string `json:"field,omitempty"`
	}
	with, _ := json.Marshal(sample{Field: "value"})
	without, _ := json.Marshal(sample{})
	fmt.Println(strings.Contains(string(with), "value"))
	fmt.Println(strings.Contains(string(without), "field"))
}
Output

Running it prints true then false.


Summary

Golang omitempty and go json omitempty mean: omit the JSON key when the Go field is the type’s zero value (plus nil pointers and empty collections). Omitempty meaning for APIs is “hide zero defaults.” For nested struct fields, remember that a value nested struct encodes as {}, so omitempty usually does not remove the outer key—that is why people say omitempty has no effect on nested struct fields in that sense. Prefer *T with nil, json.Marshaler, or explicit types when you need finer control than the default rules.


References


Frequently Asked Questions

1. What is omitempty meaning in Go JSON?

The json struct tag option omitempty tells encoding/json to skip marshaling that field when its Go value is the type zero value, or in a few special cases like nil pointers, empty slices, and empty maps. It only looks at the field value, not custom business rules.

2. Why does omitempty have no effect on nested struct fields (value types)?

A non-pointer nested struct is never nil; its zero value is a struct with zeroed fields, which still marshals as a JSON object like {}. Omitempty therefore does not drop the outer key for a zero struct value. Use a pointer *T and leave it nil to omit the whole nested object, or implement json.Marshaler for custom emptiness.

3. Does golang omitempty remove false or 0 when they matter?

Yes. false for bool, 0 for numbers, and empty string are zero values, so omitempty omits them even if they are meaningful in your API. Drop omitempty on those fields or use a pointer to bool or int and send null when unset.

4. Where do golang json omitempty and json omitempty apply?

On exported struct fields during Marshal to JSON and when matching keys during Unmarshal; the tag is part of the struct field metadata described in reflect and encoding/json documentation.
Tuan Nguyen

Data Scientist

Proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise …