Typed APIs: Safer Than Dictionaries
Ops engineers deal with JSON constantly — AWS API responses, Kubernetes object manifests, Prometheus query results, Terraform state files. In Python, you load JSON into a dictionary and access fields by string key. If you typo the key name, you get None at runtime, possibly hours later in production.
In Go, you define a struct that describes the shape of the JSON, and the compiler enforces that you access only fields that exist. Typos become compile errors, not runtime surprises.
The mapping between JSON keys and struct fields is controlled by struct tags — metadata annotations in backticks after each field.
package main
import (
"encoding/json"
"fmt"
"log"
)
// PodStatus mirrors the relevant fields from a Kubernetes Pod JSON response
type PodStatus struct {
Phase string `json:"phase"`
HostIP string `json:"hostIP"`
PodIP string `json:"podIP"`
StartTime string `json:"startTime"`
}
type Pod struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
Status PodStatus `json:"status"`
}
func main() {
// Simulated JSON from kubectl get pod -o json (simplified)
raw := []byte(`{
"name": "nginx-7d9f8b5c4-xk2p9",
"namespace": "production",
"status": {
"phase": "Running",
"hostIP": "10.0.1.15",
"podIP": "172.16.0.42",
"startTime": "2024-01-15T08:23:11Z"
}
}`)
var pod Pod
if err := json.Unmarshal(raw, &pod); err != nil {
log.Fatalf("Failed to parse pod JSON: %v", err)
}
fmt.Printf("Pod: %s/%s\n", pod.Namespace, pod.Name)
fmt.Printf("Status: %s\n", pod.Status.Phase)
fmt.Printf("Running on node: %s\n", pod.Status.HostIP)
}
Output:
Pod: production/nginx-7d9f8b5c4-xk2p9
Status: Running
Running on node: 10.0.1.15
Marshalling (Go → JSON)
The reverse operation — converting a Go struct to JSON — uses json.Marshal:
alert := map[string]interface{}{
"severity": "critical",
"message": "Disk usage above 90%",
"host": "db-01.prod",
}
data, err := json.Marshal(alert)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data))
// {"host":"db-01.prod","message":"Disk usage above 90%","severity":"critical"}
Handling unknown or optional fields
If a JSON field might be absent, use a pointer type or omitempty in the struct tag:
type Server struct {
Hostname string `json:"hostname"`
Region string `json:"region,omitempty"` // omitted from output if empty
Tags []string `json:"tags,omitempty"`
}
omitempty means: when marshalling to JSON, skip this field if it has its zero value (empty string, nil slice, 0, false). This keeps your JSON output clean.