json.Marshal converts a Go value into JSON bytes; json.Unmarshal parses JSON bytes into a Go value. Struct tags (`json:"fieldName"`) control the JSON key name for each field, and only exported (capitalized) fields participate at all — an unexported field is invisible to the JSON package and silently omitted, not an error.
Marshal: Go struct to JSON
Struct tags map Go field names to the JSON keys you actually want — without a tag, the JSON key defaults to the exact Go field name.
type User struct {
Name string `json:"name"`
Email string `json:"email"`
age int // unexported: silently OMITTED from JSON, not an error
}
u := User{Name: "Alice", Email: "alice@example.com", age: 30}
data, err := json.Marshal(u)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data))
// {"name":"Alice","email":"alice@example.com"} — no "age" at allUnmarshal: JSON to Go struct
Unmarshal takes a pointer to the destination, since it needs to write into it. Fields present in the JSON but absent from the struct are ignored by default; struct fields absent from the JSON simply keep their zero value.
jsonData := []byte(`{"name":"Bob","email":"bob@example.com","extra":"ignored"}`)
var u User
if err := json.Unmarshal(jsonData, &u); err != nil {
log.Fatal(err)
}
fmt.Println(u.Name) // Bob — "extra" key was simply ignoredCommon tag options
Beyond renaming a key, tags support omitempty (skip the field entirely if it's the zero value) and - (always exclude an exported field from JSON, when you want it in Go but never in the output).
type Profile struct {
Name string `json:"name"`
Bio string `json:"bio,omitempty"` // omitted if Bio == ""
Password string `json:"-"` // NEVER included in JSON
}