{ Soham Kamani }

AboutBlog GithubTwitter

Go's "omitempty" explained ⏺

If you’ve worked with Go for sometime, you have probably encountered the omitempty tag, when trying to encode a struct to JSON. This post will explain what exactly the omitempty tag means, and some common pitfalls, along with ways to get around them.

Basic usage

When using Go to build web applications, or HTTP servers, there will eventually come a time when we need to encode some data as JSON. The problem here is that the JSON representation does not exactly match the way Go handles data.

One key difference, is that in a struct, there are always clearly defined fields, while in JSON there are not. For example, consider describing a dog as a struct in Go:

type Dog struct {
	Breed string
	WeightKg int
}

Now, if we were to initialize, encode to JSON, and print a sample dog :

func main() {
	d := Dog{
		Breed:    "dalmation",
		WeightKg: 45,
	}
	b, _ := json.Marshal(d)
	fmt.Println(string(b))
}

We would get the output : {"Breed":"dalmation","WeightKg":45} (Try it here)

Now, a dog for whom we do not have the weight, would be slightly awkward to deal with:

func main() {
	d := Dog{
		Breed:    "pug",
	}
	b, _ := json.Marshal(d)
	fmt.Println(string(b))
}

This prints {"Breed":"pug","WeightKg":0}, even though the weight of of pug was unknown. A better option would be to print "WeightKg":null, or to not have the WeightKg key at all.

This is where the omitempty tag helps us. Let’s add the omitempty tag to our Dog struct:

type Dog struct {
	Breed    string
	// The first comma below is to separate the name tag from the omitempty tag 
	WeightKg int `json:",omitempty"`
}

Now, if WeightKg is set to it’s default value (which is 0 for the int type), the key itself will be omitted from the JSON object.

The same will happen if a string is empty "", or if a pointer is nil, or if a slice has zero elements in it.

Values that cannot be omitted

In cases where an empty value does not exist, omitempty is of no use. An embedded struct, for example, does not have an empty value:

type dimension struct {
	Height int
	Width int
	}

type Dog struct {
	Breed    string
	WeightKg int
	Size dimension `json:",omitempty"`
}

func main() {
	d := Dog{
		Breed: "pug",
	}
	b, _ := json.Marshal(d)
	fmt.Println(string(b))
}

This gives the output:

{"Breed":"pug","WeightKg":0,"Size":{"Height":0,"Width":0}}

In this case, even though we never set the value of the Size attribute, and set its omitempty tag, it still appears in the output. This is because structs do not have any empty value in Go. To solve this, use a struct pointer instead :

type Dog struct {
	Breed    string
	WeightKg int
	// Now `Size` is a pointer to a `dimension` instance
	Size *dimension `json:",omitempty"`
}

Running the same code will now give us:

{"Breed":"pug","WeightKg":0}

Although structs do not have an “empty” value, struct pointers do, with the empty value being nil.

The difference between 0, "" and nil

One issue which particularly caused me a lot a trouble is the case where you want to differentiate between a default value, and a zero value.

For example, if we have a struct describing a resteraunt, with the number of seated customers as an attribute:

type Restaurant struct {
	NumberOfCustomers int `json:",omitempty"`
}

func main() {
	d := Restaurant{
		NumberOfCustomers: 0,
	}
	b, _ := json.Marshal(d)
	fmt.Println(string(b))
}

Running the above code will print:

{}

Try it here

In this case, the number of customers in a restaurant can actually be 0, and we would want to convey this information in the JSON object. At the same time, if the NumberOfCustomers is not set, we would like the key to be omitted. In other words, _we do not want 0 to be the “empty” value for NumberOfCustomers.

One way to solve this is to use an int pointer:

type Restaurant struct {
	NumberOfCustomers *int `json:",omitempty"`
}

Now the empty value is a nil pointer, which is different from the value of 0:

func main() {
	d1 := Restaurant{}
	b, _ := json.Marshal(d1)
	fmt.Println(string(b))
	//Prints: {}
	
	n := 0
	d2 := Restaurant{
		NumberOfCustomers: &n,
	}
	b, _ = json.Marshal(d2)
	fmt.Println(string(b))
	//Prints: {"NumberOfCustomers":0}
}

When to use “omitempty”

Although we’ve gone through the usage of omitempty, the most important thing to remember is that it should be used sparingly. If the application consuming the JSON objects generated by the Go application doesn’t differentiate between undefined keys and zero value keys, then there’s really no reason to use the omitempty tag in the first place.

If you do decide to use it, then make sure that Go’s definition of “empty” matches your application’s.


Like what I write? Join my mailing list, and I'll let you know whenever I write another post

Comments

Soham Kamani

Written by Soham Kamani, an author,and a full-stack developer who has extensive experience in the JavaScript ecosystem, and building large scale applications in Go. He is an open source enthusiast and an avid blogger. You should follow him on Twitter