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.