Question

Go Struct Tags: Using Backticks for JSON Field Names

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"`
  • Gateway is the Go field name.
  • string is 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 box gateway.”

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.

Take Quiz

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" uses name as the JSON key.
  • json:"-" excludes the field from JSON.
  • json:"nickname,omitempty" omits nickname when 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:

  1. Go defines the Device type with two exported fields and their tags.

  2. device is created with Name set to "router" and Enabled set to true.

  3. json.Marshal examines the struct fields.

  4. The JSON package reads the tags:

    • Name has json:"name", so its JSON key is name.
    • Enabled has json:"enabled", so its JSON key is enabled.
  5. It creates JSON data:

    {"name":"router","enabled":true}
    
  6. fmt.Println prints 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, or ip_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.

Take Quiz

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

ConstructPurposeAffects encoding/json?
// Gateway addressA source-code comment for readersNo
Gateway stringAn exported Go struct fieldYes, using the default name Gateway
Gateway string + "json:\"gateway\"" + `A field plus JSON metadataYes, uses gateway
Gateway string + "json:\"-\"" + `A field excluded from JSONYes, field is omitted
Gateway string + "json:\"gateway,omitempty\"" + `A field with a name and an omission optionYes

Backticks versus double quotes in Go strings

String formExampleTypical 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 concernExample
Field name used by Gonetwork.IPAddress
JSON key used outside Go"ip"
Tag connecting them`json:"ip"`
Take Quiz

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/json reads json:"..." tags.
  • Tags are not comments.
  • JSON keys commonly use snake_case or lowercase; Go exported fields use PascalCase.
  • Fields must be exported for standard JSON marshaling and unmarshaling.
  • json:"-" means ignore this field.
  • omitempty omits 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.

Take Quiz

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

  1. Define a NetworkDevice struct with fields for hostname, IP address, prefix length, MAC address, and API token.
  2. Use JSON tags so the output uses hostname, ip, ip_prefix_len, and mac_address.
  3. Exclude the API token from JSON output.
  4. Create a sample device value and marshal it with encoding/json.
  5. Print the formatted JSON result.
Take Quiz

Keep learning

Related questions

View more