Skip to main content

JSON in Go

Learning Focus

Go's encoding/json is fast and safe. Struct tags give you explicit control over the JSON mapping — making serialization predictable and testable.

Struct Mapping

package main

import (
"encoding/json"
"fmt"
"time"
)

type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
IsActive bool `json:"isActive"`
CreatedAt time.Time `json:"createdAt"`
Password string `json:"-"` // never serialized
Notes string `json:"notes,omitempty"` // omit if empty
}

Unmarshal (Parse)

raw := []byte(`{"id":1,"name":"Alice","email":"alice@example.com","isActive":true}`)
var u User
if err := json.Unmarshal(raw, &u); err != nil {
log.Fatal(err)
}
fmt.Println(u.Name) // Alice

Marshal (Serialize)

user := User{ID: 2, Name: "Bob", Email: "bob@example.com", IsActive: true}
out, _ := json.MarshalIndent(user, "", " ")
fmt.Println(string(out))

Streaming with json.Decoder

resp, _ := http.Get("https://api.example.com/users")
defer resp.Body.Close()

var users []User
if err := json.NewDecoder(resp.Body).Decode(&users); err != nil {
log.Fatal(err)
}

Dynamic JSON: map[string]interface{}

var dynamic map[string]interface{}
json.Unmarshal(raw, &dynamic)

name := dynamic["name"].(string) // type assertion required

Struct Tag Reference

TagEffect
json:"name"Use name as JSON key
json:"-"Exclude field
json:",omitempty"Omit if zero value
json:",string"Encode number/bool as string

Concept Map

Concept Flow

JSON bytes → json.Unmarshal → Go struct / map → Business Logic → json.MarshalIndent → JSON bytes
HTTP Body → json.NewDecoder → Go struct / map

Common Pitfalls

PitfallConsequencePrevention
Unexported (lowercase) fieldsSilently not marshaledUse exported (capitalized) names
Missing omitempty on pointersZero values output as nullAdd omitempty or handle explicitly
interface{} type assertionsPanic on wrong typeCheck ok: v, ok := d["x"].(string)
Non-pointer to UnmarshalFields not populatedPass &target to json.Unmarshal

What's Next