How do struct tags work, and how are they used with reflection?

5 minintermediategostruct-tagsreflectionjson

Quick Answer

A struct tag is a string literal attached to a struct field, written after its type: Name string \json:"name" validate:"required"`. Tags are just metadata — Go itself does nothing with them at compile time. Libraries read them at runtime through the reflectpackage'sStructField.Tag and a small parsing convention (key:"value"pairs).encoding/jsonreadsjson:"..." tags to control field names, omission (omitempty), and skipping (json:"-"); validation libraries read their own tag keys the same way. Because tags are plain strings, a typo like jsno:"name"` compiles fine but silently does nothing, which is the most common tag-related bug.

Detailed Answer

Struct tags are how Go bolts on metadata-driven behavior, like JSON field names, without adding any new syntax to the language itself.

A typical tagged struct

type User struct {
    ID        int    `json:"id"`
    Name      string `json:"name"`
    Email     string `json:"email,omitempty"`
    Password  string `json:"-"`              // never serialized
}
u := User{ID: 1, Name: "Alice", Password: "secret"}
b, _ := json.Marshal(u)
fmt.Println(string(b))  // {"id":1,"name":"Alice"}

omitempty skips the field if it holds its zero value; json:"-" skips it unconditionally, which is exactly how you keep a sensitive field like a password hash out of API responses.

How a library actually reads a tag

t := reflect.TypeOf(User{})
field, _ := t.FieldByName("Name")
fmt.Println(field.Tag.Get("json"))  // "name"

encoding/json, and most tag-driven libraries, use reflect to inspect each field's Tag string at runtime and parse out the value for the key they care about (json, validate, db, etc.).

The silent-failure gotcha

type User struct {
    Name string `jsno:"name"`  // typo: key should be "json"
}

This compiles cleanly — the tag is just a string literal, and the compiler doesn't know or care what keys are meaningful. encoding/json simply won't find a json tag on this field, and falls back to using the Go field name (Name) as the JSON key instead. There's no compiler warning for a misspelled tag key, which makes careful review (or a linter that understands struct tags) worthwhile.

Related Resources