Skip to content

encoding/json

The encoding/json package converts Go values to JSON text (marshaling) and JSON text back to Go values (unmarshaling). The two central functions are:

data, err := json.Marshal(v) // Go value → []byte (JSON)
err = json.Unmarshal(data, &v) // []byte (JSON) → Go value

Marshal takes any value and returns a []byte. Unmarshal writes into the value pointed to by the second argument — always pass a pointer so the function can modify the variable.

A struct tag is a raw-string literal on a struct field that provides metadata. The encoding/json package reads json:"name" tags to control serialization:

type User struct {
Name string `json:"name"`
Email string `json:"email,omitempty"`
Age int `json:"age"`
}

The tag syntax is `json:"<name>[,options]"`. Common options:

TagEffect
json:"name"Use name as the JSON key instead of the field name
json:"name,omitempty"Omit this field from the output when its value is the zero value
json:"-"Always omit this field

Struct fields can themselves be structs. json.Marshal recurses into them automatically:

type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Address Address `json:"address"`
}

The resulting JSON nests the address object under the "address" key.

When you do not have a struct type ahead of time, unmarshal into map[string]any. Each JSON value becomes its natural Go counterpart: string, float64, bool, []any, map[string]any, or nil.

var m map[string]any
json.Unmarshal([]byte(`{"lang":"Go","version":1.22}`), &m)
fmt.Println(m["lang"]) // Go
fmt.Println(m["version"]) // 1.22 (float64)
package main
import (
"encoding/json"
"fmt"
)
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email,omitempty"`
Address Address `json:"address"`
}
func main() {
// Marshal a struct — Email is set, so it appears
u := User{
Name: "Alice",
Age: 30,
Address: Address{
City: "Bangkok",
Country: "Thailand",
},
}
data, err := json.Marshal(u)
if err != nil {
fmt.Println("marshal error:", err)
return
}
fmt.Println(string(data))
// omitempty: Email is empty, so it is omitted
u2 := User{Name: "Bob", Age: 25, Address: Address{City: "London", Country: "UK"}}
data2, _ := json.Marshal(u2)
fmt.Println(string(data2))
// Unmarshal back into a struct
var result User
err = json.Unmarshal(data, &result)
if err != nil {
fmt.Println("unmarshal error:", err)
return
}
fmt.Printf("Name: %s, City: %s\n", result.Name, result.Address.City)
// map[string]any — flexible unmarshaling
raw := `{"lang":"Go","version":1.22,"stable":true}`
var m map[string]any
_ = json.Unmarshal([]byte(raw), &m)
fmt.Println(m["lang"], m["version"], m["stable"])
}
What does json.Unmarshal require as its second argument?
A struct field tagged `json:"email,omitempty"` with an empty string value will:
You unmarshal JSON into map[string]any. What Go type does the number 1.22 become?
A struct field named userID (lowercase u) with a json:"user_id" tag will: