Module lessons (5/5)
`encoding/json`
encoding/json is the canonical package for serializing Go structs to JSON and back. Knowing its conventions and pitfalls saves you hours of debugging on APIs and configuration files.
Marshal and Unmarshal
The two main functions:
data, err := json.Marshal(v) // Go → []byte JSON
err = json.Unmarshal(data, &v) // JSON → Go (pass a pointer!)type User struct {
Name string
Email string
}
u := User{Name: "Ada", Email: "ada@example.com"}
b, _ := json.Marshal(u)
fmt.Println(string(b))
// {"Name":"Ada","Email":"ada@example.com"}
var back User
_ = json.Unmarshal(b, &back)For indented output (human-readable) use json.MarshalIndent(v, "", " ").
Exported fields only
json.Marshal serializes only exported fields (Uppercase). Private fields are ignored silently:
type U struct {
Name string // exported → serialized
age int // private → ignored
}Struct tags: controlling name and options
The json:"..." tags control name, omission and types in the output:
type User struct {
Name string `json:"name"` // rename
Email string `json:"email,omitempty"` // omit if zero value
Private string `json:"-"` // never serialized
ID int `json:"id,string"` // number as string
}Most used options:
,omitempty— omits the field if it is the zero value (0, "", nil, empty slice/map).,-— never serialized/deserialized.,string— wraps numbers/bools as strings (useful for JS IDs).
Decoder/Encoder for streams
When working with io.Reader/io.Writer (HTTP body, huge files) use Decoder and Encoder instead of loading everything in memory:
dec := json.NewDecoder(resp.Body)
var users []User
if err := dec.Decode(&users); err != nil {
return err
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
_ = enc.Encode(users)Decoder also supports dec.Token() for streaming piece-by-piece parsing and dec.DisallowUnknownFields() to reject JSON with unexpected keys (useful for input validation).
Generic maps and any
If you don't know the schema, deserialize into map[string]any:
var v map[string]any
_ = json.Unmarshal(data, &v)
fmt.Println(v["name"])Values will have dynamic types (string, float64 for numbers, []any, map[string]any, bool, nil).
Common errors
- Forgetting
&:json.Unmarshal(data, v)does not populate anything; you need&v. - Decoding structs with nil fields: to avoid panics, check
errBEFORE reading fields. - Incompatible types: deserializing a number into a string or vice versa returns
*json.UnmarshalTypeError.
Exercises
Serialize the struct u to JSON with json.Marshal and print the result as a string.
Solution available after 3 attempts
Deserialize `{"Name":"Ada"}` into the struct U with json.Unmarshal and print it.
Solution available after 3 attempts
Is the field `name string` (lowercase) of a struct included by json.Marshal?
type U struct {
name string
}
b, _ := json.Marshal(U{name: "Ada"})
fmt.Println(string(b))