Question
Given this Go struct:
type NetworkInterface struct {
Gateway string `json:"gateway"`
IPAddress string `json:"ip"`
IPPrefixLen int `json:"ip_prefix_len"`
MacAddress string `json:"mac"`
}
What is the purpose of the text inside backticks, such as json:"gateway"? Is it only a comment describing the field, or does it affect program behavior?
Short Answer
Go backticks in a struct field declaration contain a struct tag. A struct tag is metadata stored with the field. Packages such as encoding/json can read that metadata using reflection to control how values are encoded and decoded. For example, json:"gateway" tells the JSON package to use "gateway" as the JSON key for the Gateway field.
Concept
A Go struct field may include an optional tag, written as a string literal immediately after its type:
FieldName FieldType `tag content`
In the original example:
Gateway string `json:"gateway"`
Gatewayis the Go field name.stringis the field type.`json:"gateway"`is the struct tag.
The backticks create a raw string literal, which makes tag syntax convenient because quotation marks inside the tag do not need extra escaping. The same tag could be written as an interpreted string literal, but it is harder to read:
Gateway string "json:\"gateway\""
A tag is not a comment. Go stores it as part of the field's type information, so code can inspect it at runtime through the reflect package.
The Go standard library's encoding/json package recognizes the json tag key. When marshaling a struct, it uses the tag to choose JSON property names and options. When unmarshaling JSON, it uses the tag to map incoming JSON properties back to struct fields.
Without the tag, encoding/json normally uses the exported Go field name, such as "Gateway". With json:"gateway", it uses lowercase "gateway" instead.
Mental Model
Think of a struct as a paper form and each field as a labeled box.
- The Go field name, such as
Gateway, is the label used by Go code. - A struct tag is an instruction sticker attached to that box.
json:"gateway"tells a JSON converter: “When you create or read JSON, call this boxgateway.”
The Go compiler does not automatically change behavior because of a tag. Instead, a package must know how to read and obey that particular sticker. encoding/json understands json tags; other packages may ignore them completely.
Syntax and Examples
The general form is:
type TypeName struct {
FieldName FieldType `key:"value"`
}
For JSON, use the json key:
package main
import (
"encoding/json"
"fmt"
)
type NetworkInterface struct {
Gateway string `json:"gateway"`
IPAddress string `json:"ip"`
IPPrefixLen int `json:"ip_prefix_len"`
MacAddress string `json:"mac"`
}
func main() {
network := NetworkInterface{
Gateway: "192.168.1.1",
IPAddress: "192.168.1.50",
IPPrefixLen: 24,
MacAddress: "00:11:22:33:44:55",
}
data, err := json.Marshal(network)
if err != nil {
panic(err)
}
fmt.Println(string(data))
}
Output:
{"gateway":"192.168.1.1","ip":"192.168.1.50","ip_prefix_len":24,"mac":"00:11:22:33:44:55"}
The tags make the JSON keys gateway, ip, ip_prefix_len, and mac. They are especially useful when an API uses naming conventions that differ from Go's exported PascalCase field names.
JSON tag options
A tag can include options after a comma:
type User struct {
Name string `json:"name"`
Password string `json:"-"`
Nickname string `json:"nickname,omitempty"`
}
json:"name"usesnameas the JSON key.json:"-"excludes the field from JSON.json:"nickname,omitempty"omitsnicknamewhen it has its empty value, such as""for a string.
Step by Step Execution
Consider this program:
package main
import (
"encoding/json"
"fmt"
)
type Device struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
}
func main() {
device := Device{Name: "router", Enabled: true}
data, err := json.Marshal(device)
if err != nil {
panic(err)
}
fmt.Println(string(data))
}
Execution flow:
-
Go defines the
Devicetype with two exported fields and their tags. -
deviceis created withNameset to"router"andEnabledset totrue. -
json.Marshalexamines the struct fields. -
The JSON package reads the tags:
Namehasjson:"name", so its JSON key isname.Enabledhasjson:"enabled", so its JSON key isenabled.
-
It creates JSON data:
{"name":"router","enabled":true} -
fmt.Printlnprints that JSON string.
The tag does not store a second value and does not rename the Go field. In Go code, you still write device.Name, not device.name.
Real World Use Cases
Struct tags are commonly used at boundaries where Go data must match another system's format.
- REST and JSON APIs: Match API keys such as
user_id,created_at, orip_prefix_len. - Reading API requests: Decode JSON sent by a browser or mobile app into a Go struct.
- Database mapping: Libraries may use tags such as
db:"user_id"to map fields to SQL columns. - Configuration files: YAML and TOML libraries often support tags like
yaml:"server_port". - Form validation: Validation libraries may read tags such as
validate:"required,email". - ORM configuration: Some ORM libraries use tags to define primary keys, relationships, or column behavior.
One field can carry tags for multiple tools:
type Account struct {
ID int `json:"id" db:"account_id"`
Email string `json:"email" validate:"required,email"`
}
Each library reads the key it understands. The JSON package reads json; a database package may read db; a validation package may read validate.
Real Codebase Usage
In production Go code, JSON tags are usually placed on request, response, configuration, and persistence structs so external names stay stable even if internal Go names are clearer or change later.
Match an external API contract
type CreateUserRequest struct {
Email string `json:"email"`
FullName string `json:"full_name"`
}
The API can consistently accept full_name, while Go code uses the idiomatic FullName field.
Exclude private or sensitive data
type UserResponse struct {
ID string `json:"id"`
Email string `json:"email"`
PasswordHash string `json:"-"`
}
json:"-" is a safety measure: it prevents PasswordHash from being serialized in an HTTP response.
Omit optional response values
type ErrorResponse struct {
Message string `json:"message"`
Code string `json:"code,omitempty"`
}
If Code is empty, omitempty prevents an unnecessary "code":"" property.
Decode and validate at the application boundary
type LoginRequest struct {
Email string `json:"email"`
Password string `json:"password"`
}
A handler decodes JSON into this type, then validates values before performing application logic. Tags define the transport format; they do not validate data by themselves.
Common Mistakes
Treating tags as comments
Tags may look descriptive, but packages can read them and change serialization behavior.
// This is a real JSON mapping instruction, not a comment.
Gateway string `json:"gateway"`
Use // comments when you only want to explain code to people.
Using unexported fields
The standard JSON package only marshals exported struct fields. Exported names begin with an uppercase letter.
type Config struct {
host string `json:"host"` // Ignored by encoding/json.
}
Use an exported field instead:
type Config struct {
Host string `json:"host"`
}
Writing an invalid tag
This is malformed because the tag value is missing quotation marks:
type User struct {
Name string `json:name`
}
Use the key:"value" format:
type User struct {
Name string `json:"name"`
}
Expecting a tag to rename the Go field
A JSON tag does not change how the field is accessed in Go:
user.Name = "Ada" // Correct
// user.name = "Ada" // Incorrect: no field named name
Forgetting that omitempty can hide zero values
type Product struct {
Stock int `json:"stock,omitempty"`
}
When Stock is 0, the JSON output has no stock field. That is wrong if zero stock must be sent explicitly. Remove omitempty when the zero value is meaningful.
Assuming every package recognizes every tag
encoding/json understands json tags, not db or yaml tags. A tag has an effect only when the library you use supports its key.
Comparisons
| Construct | Purpose | Affects encoding/json? |
|---|---|---|
// Gateway address | A source-code comment for readers | No |
Gateway string | An exported Go struct field | Yes, using the default name Gateway |
Gateway string + "json:\"gateway\"" + ` | A field plus JSON metadata | Yes, uses gateway |
Gateway string + "json:\"-\"" + ` | A field excluded from JSON | Yes, field is omitted |
Gateway string + "json:\"gateway,omitempty\"" + ` | A field with a name and an omission option | Yes |
Backticks versus double quotes in Go strings
| String form | Example | Typical use |
|---|---|---|
| Raw string literal | `json:"gateway"` | Struct tags, regular expressions, multiline text |
| Interpreted string literal | "json:\"gateway\"" | Normal strings that may use escape sequences |
Backticks are common for tags because they avoid escaping the double quotes that tag parsers expect.
Tags versus field names
| Go code concern | Example |
|---|---|
| Field name used by Go | network.IPAddress |
| JSON key used outside Go | "ip" |
| Tag connecting them | `json:"ip"` |
Cheat Sheet
type Example struct {
// JSON key is "name".
Name string `json:"name"`
// Excluded from JSON input and output.
Secret string `json:"-"`
// Omitted when empty ("", 0, false, nil, etc.).
Note string `json:"note,omitempty"`
}
- A struct tag is metadata after a struct field type.
- Backticks create the raw string literal that contains the tag.
encoding/jsonreadsjson:"..."tags.- Tags are not comments.
- JSON keys commonly use
snake_caseor lowercase; Go exported fields usePascalCase. - Fields must be exported for standard JSON marshaling and unmarshaling.
json:"-"means ignore this field.omitemptyomits empty values; do not use it when zero values must be represented.- Tags do nothing unless a package reads the relevant tag key.
FAQ
What do backticks mean in a Go struct?
Backticks delimit a raw string literal. After a struct field, that string is used as the field's struct tag.
Is json:"gateway" a comment in Go?
No. It is metadata that encoding/json can read to map the Go field to the JSON key gateway.
Why does Go use backticks for struct tags?
Tags often contain double quotes, such as json:"name". A raw string literal avoids writing escape characters around those quotes.
What happens if I remove a JSON tag?
For exported fields, encoding/json usually uses the Go field name. For example, Gateway becomes "Gateway" rather than "gateway".
Can I use multiple struct tags on one Go field?
Yes. Separate tag entries with spaces:
Email string `json:"email" db:"email_address"`
Each library reads the tag key it supports.
Does a JSON tag make a field private?
No. Go visibility depends on whether the field name begins with an uppercase letter. Use json:"-" to exclude a field from JSON.
Does omitempty omit a value of zero?
Yes. It omits empty values, including 0 for numbers, false for booleans, "" for strings, and nil for pointers, maps, slices, and interfaces.
Do struct tags work without reflection?
Tags are stored in type metadata. Most libraries, including encoding/json, inspect them through reflection.
Mini Project
Description
Build a small API response model for a network device. The project demonstrates how JSON tags map idiomatic Go field names to the lowercase and snake_case keys expected by an external API, while preventing sensitive data from being exposed.
Goal
Create and serialize a network device response that uses JSON field names defined by struct tags.
Requirements
- Define a
NetworkDevicestruct with fields for hostname, IP address, prefix length, MAC address, and API token. - Use JSON tags so the output uses
hostname,ip,ip_prefix_len, andmac_address. - Exclude the API token from JSON output.
- Create a sample device value and marshal it with
encoding/json. - Print the formatted JSON result.
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.