encoding/json

Guided tour · Encoding · pkg.go.dev →

Encode and decode JSON. Works with structs via reflection and tags, or maps/any for dynamic shapes.

Encode/decode Go values to JSON. Match wire format with struct tags. For huge documents, prefer the streaming Encoder/Decoder.

Encode to bytes
b, err := json.Marshal(v)
Pretty print
b, err := json.MarshalIndent(v, "", "  ")
Decode from bytes
err := json.Unmarshal(b, &v)
Stream encode to Writer
err := json.NewEncoder(w).Encode(v)
Stream decode from Reader
err := json.NewDecoder(r).Decode(&v)
Optional field tag
Field string `json:"field,omitempty"`
Skip unknown fields safely
dec := json.NewDecoder(r)
dec.DisallowUnknownFields()
err := dec.Decode(&v)

Marshal and Unmarshal

Marshal goes value → []byte. Unmarshal goes []byte → pointer. Always pass a pointer to Unmarshal.

Struct marshaling with tags

json:"name" renames the field. omitempty skips zero values. A field must be exported (capitalized) to be seen by the encoder.

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

b, _ := json.Marshal(User{Name: "Ada"})
fmt.Println(string(b))   // {"name":"Ada"}
Output
{"name":"Ada"}

MarshalIndent — pretty-printed

b, _ := json.MarshalIndent(User{Name: "Ada", Age: 36}, "", "  ")
fmt.Println(string(b))
Output
{
  "name": "Ada",
  "age": 36
}

Unmarshal into a struct

data := []byte(`{"name":"Ada","age":36}`)
var u User
if err := json.Unmarshal(data, &u); err != nil {
    log.Fatal(err)
}
fmt.Println(u)

Unmarshal into a map or any

Use when the shape isn't known. Numbers decode as float64 unless you use json.Decoder.UseNumber().

var v any
json.Unmarshal([]byte(`{"a":1,"b":[2,3]}`), &v)
fmt.Printf("%#v\n", v)

Streaming with Encoder / Decoder

Use these for JSON over the wire (HTTP, TCP, files). Decoder reads from an io.Reader; Encoder writes to an io.Writer.

Decoder — stream a JSON array

dec := json.NewDecoder(resp.Body)
_, _ = dec.Token()   // read the opening [
for dec.More() {
    var u User
    if err := dec.Decode(&u); err != nil {
        log.Fatal(err)
    }
    fmt.Println(u)
}

Encoder — write a JSON response

Encoder adds a trailing newline after each value, which is what you usually want for HTTP JSON lines.

func handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(User{Name: "Ada", Age: 36})
}

DisallowUnknownFields — strict decoding

Reject payloads with fields your struct doesn't declare. Great for config files and public APIs.

dec := json.NewDecoder(r)
dec.DisallowUnknownFields()
if err := dec.Decode(&cfg); err != nil {
    return err
}

Custom encoding

Implement json.Marshaler / Unmarshaler

type Celsius float64

func (c Celsius) MarshalJSON() ([]byte, error) {
    return []byte(fmt.Sprintf("\"%.1f°C\"", float64(c))), nil
}

b, _ := json.Marshal(Celsius(22.5))
fmt.Println(string(b))
Output
"22.5°C"

RawMessage — defer decoding

Use when the shape of a sub-object depends on another field (e.g. a 'type' discriminator).

type Envelope struct {
    Kind    string          `json:"kind"`
    Payload json.RawMessage `json:"payload"`
}

var env Envelope
json.Unmarshal(data, &env)

switch env.Kind {
case "user":
    var u User
    json.Unmarshal(env.Payload, &u)
case "order":
    var o Order
    json.Unmarshal(env.Payload, &o)
}

Valid and compact

json.Valid

json.Valid([]byte(`{"ok":true}`))    // true
json.Valid([]byte("{oops"))          // false

json.Indent / json.Compact

var out bytes.Buffer
json.Indent(&out, raw, "", "  ")   // pretty-print
// json.Compact(&out, raw)         // remove whitespace