1. Struct Tags & Field Name Mapping

In Go, struct fields MUST begin with a capital letter to be exported and accessible to the `encoding/json` package. Use struct tags (`json:"key_name"`) to specify exact JSON key names.

Go Struct Definition with JSON Tags
package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID        int64    `json:"user_id"`
    Username  string   `json:"username"`
    Email     string   `json:"email,omitempty"` // Omit if empty string
    IsActive  bool     `json:"is_active"`
    SecretKey string   `json:"-"`               // Ignore field completely
}

func main() {
    u := User{ID: 101, Username: "gopher", IsActive: true}
    
    // Marshal struct to formatted JSON bytes
    data, _ := json.MarshalIndent(u, "", "  ")
    fmt.Println(string(data))
}
Explanation: `json:"user_id"` maps the Go struct field `ID` to the lower-snake-case JSON property `user_id`.

2. Unmarshaling JSON Bytes into Go Structs (`json.Unmarshal`)

To decode a JSON byte slice into a Go struct, pass a pointer (`&struct`) to `json.Unmarshal()`. Always handle returned error values explicitly.

Decoding JSON String to Go Struct
jsonBytes := []byte(`{"user_id": 202, "username": "alice_dev", "is_active": true}`)

var user User
err := json.Unmarshal(jsonBytes, &user)
if err != nil {
    log.Fatalf("JSON Unmarshal Error: %v", err)
}

fmt.Printf("Parsed User ID: %d, Name: %s\n", user.ID, user.Username)
Explanation: Passing `&user` pointer allows `json.Unmarshal` to mutate the target struct instance in memory.

3. Streaming Decoders (`json.NewDecoder`) for HTTP APIs

When handling incoming HTTP request bodies in Net/HTTP handlers or Gin frameworks, use `json.NewDecoder(r.Body).Decode(&dst)` to decode JSON directly from the request stream without allocating intermediate byte buffers.

Pro Tips

  • Use `decoder.DisallowUnknownFields()` to reject HTTP requests containing unexpected client fields.
  • Use `json.Encoder` when writing API responses directly to `http.ResponseWriter`.