Skip to main content
eLearner.app
Module 8 · Lesson 5 of 540/50 in the course~14 min
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:

Go
data, err := json.Marshal(v)      // Go → []byte JSON
err = json.Unmarshal(data, &v)    // JSON → Go (pass a pointer!)
Go
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:

Go
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:

Go
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:

Go
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:

Go
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 err BEFORE reading fields.
  • Incompatible types: deserializing a number into a string or vice versa returns *json.UnmarshalTypeError.

Exercises

Exercise#go.m8.l5.e1
Attempts: 0Loading…

Serialize the struct u to JSON with json.Marshal and print the result as a string.

Loading editor…

Solution available after 3 attempts

Exercise#go.m8.l5.e2
Attempts: 0Loading…

Deserialize `{"Name":"Ada"}` into the struct U with json.Unmarshal and print it.

Loading editor…

Solution available after 3 attempts

Quiz#go.m8.l5.e3
Ready

Is the field `name string` (lowercase) of a struct included by json.Marshal?

Go
type U struct {
  name string
}
b, _ := json.Marshal(U{name: "Ada"})
fmt.Println(string(b))
Answer options