2025-06-15
Go Structs and JSON Tags: The Complete Guide
Go's struct tags look like magic but follow simple rules. Master them and you'll never have a JSON serialization surprise again.
What Are Struct Tags?
Go struct tags are string literals attached to struct fields that provide metadata to reflection-based libraries:
type User struct {
ID int64 `json:"id"`
FullName string `json:"full_name"`
Email string `json:"email,omitempty"`
password string // unexported — never serialized
}
The encoding/json package reads these tags to map between Go field names and JSON keys.
The omitempty Directive
omitempty skips the field during marshaling if it holds the zero value for its type (0, "", false, nil):
Email string `json:"email,omitempty"`
This is essential for optional fields in PATCH requests — you don't want to send "email": "" when the user didn't touch the email field.
Gotcha: omitempty doesn't work as expected with structs. An empty struct {} is never omitted. Use a pointer *Address instead.
The - Tag: Explicitly Exclude Fields
type User struct {
Password string `json:"-"`
}
The "-" tag completely excludes the field from both marshaling and unmarshaling. Never rely on unexported fields alone — a future refactor could accidentally export one.
Pointer Receivers for Nullable Fields
type User struct {
DeletedAt *time.Time `json:"deleted_at,omitempty"`
}
A nil pointer marshals to JSON null. Combined with omitempty, a nil pointer is omitted entirely. This gives you three states: present, null, and absent.
Common Mistakes
- Forgetting omitempty on pagination cursors — sends
"cursor": ""which breaks many APIs - Using struct tags on embedded structs — tags on the embedded struct, not the embedding one
- Assuming case-insensitive unmarshaling always works — it does, but it's slower; use exact tags for performance-critical paths
Our converter generates Go structs with correct JSON tags automatically, including omitempty when the toggle is enabled.