Go JSON & HTTP

JSON Encoding

Marshal struct to JSON
type Host struct {
    Name   string `json:"name"`
    IP     string `json:"ip"`
    Port   int    `json:"port,omitempty"`
    Active bool   `json:"active"`
}

h := Host{Name: "sw01", IP: "10.50.1.10", Port: 22, Active: true}

// Compact
data, err := json.Marshal(h)
// {"name":"sw01","ip":"10.50.1.10","port":22,"active":true}

// Pretty
data, err := json.MarshalIndent(h, "", "  ")

omitempty omits zero-value fields. Use json:"-" to exclude a field entirely.

JSON Decoding

Unmarshal JSON into a struct
input := `{"name":"sw01","ip":"10.50.1.10","port":22}`
var h Host
err := json.Unmarshal([]byte(input), &h)
if err != nil {
    log.Fatal(err)
}
fmt.Println(h.Name)  // "sw01"

// Decode from io.Reader (HTTP response, file)
dec := json.NewDecoder(resp.Body)
var result Host
err := dec.Decode(&result)

json.NewDecoder reads from any io.Reader — more efficient than reading all bytes first.

Decode into map for unknown structure
var data map[string]any
err := json.Unmarshal(raw, &data)
name := data["name"].(string)  // type assertion needed

HTTP Server

Minimal HTTP server
func main() {
    http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
    })

    http.HandleFunc("/hosts", hostsHandler)

    log.Println("listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func hostsHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        hosts := getHosts()
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(hosts)
    case http.MethodPost:
        var h Host
        if err := json.NewDecoder(r.Body).Decode(&h); err != nil {
            http.Error(w, err.Error(), http.StatusBadRequest)
            return
        }
        // process h...
        w.WriteHeader(http.StatusCreated)
    default:
        http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
    }
}

json.NewEncoder(w).Encode(v) writes JSON directly to the response writer — no intermediate byte slice.

HTTP Client

Making HTTP requests
// Simple GET
resp, err := http.Get("https://httpbin.org/get")
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))

// Custom request with headers
client := &http.Client{Timeout: 10 * time.Second}

req, _ := http.NewRequest("GET", "https://api.example.com/hosts", nil)
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Accept", "application/json")

resp, err := client.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result []Host
json.NewDecoder(resp.Body).Decode(&result)

Always set a timeout on http.Client. The default client has no timeout — a hung server will block forever.

POST with JSON body
h := Host{Name: "sw02", IP: "10.50.1.11", Port: 22}
body, _ := json.Marshal(h)

resp, err := http.Post(
    "https://api.example.com/hosts",
    "application/json",
    bytes.NewReader(body),
)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()
fmt.Println("Status:", resp.StatusCode)

File I/O

Read and write files
// Read entire file
data, err := os.ReadFile("/etc/hostname")
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(data))

// Write entire file (creates or truncates)
err := os.WriteFile("/tmp/output.txt", []byte("hello\n"), 0644)

// Line-by-line reading
f, _ := os.Open("/etc/hosts")
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
    fmt.Println(scanner.Text())
}

os.ReadFile and os.WriteFile are the simple path. Use bufio.Scanner for line-by-line processing of large files.