Infrastructure Go daemon
SECTION 4.3

Parsing YAML & JSON

Infrastructure is YAML and JSON all the way down. K8s manifests, Terraform configs, CI pipelines, Helm values.

1 · Before you read — commit to an answer
type Pod struct {
    Name     string `json:"name"`
    Replicas int    `json:"replicas"`
}

func main() { data := []byte({"name":"web","memory":512}) var pod Pod json.Unmarshal(data, &pod) fmt.Println(pod.Name, pod.Replicas) }

Commit first: what happens to the field the JSON has but the struct doesn't, and the field the struct has but the JSON doesn't?

JSON with encoding/json

2 · Worked example — read every step
import "encoding/json"

type Pod struct {
    Name      string `json:"name"`
    Namespace string `json:"namespace"`
    Image     string `json:"image,omitempty"` // omit if empty
    Replicas  int    `json:"replicas"`
}

// Unmarshal: JSON bytes → struct
data := []byte(`{"name":"web","namespace":"prod","replicas":3}`)
var pod Pod
if err := json.Unmarshal(data, &pod); err != nil {
    return fmt.Errorf("parsing JSON: %w", err)
}

// Marshal: struct → JSON bytes
out, err := json.MarshalIndent(pod, "", "  ")
if err != nil {
    return fmt.Errorf("encoding JSON: %w", err)
}
fmt.Println(string(out))

Struct Tags

Tags control how fields map to JSON/YAML keys:

type Config struct {
    APIVersion string            `json:"apiVersion" yaml:"apiVersion"`
    Kind       string            `json:"kind" yaml:"kind"`
    Metadata   Metadata          `json:"metadata" yaml:"metadata"`
    Spec       map[string]any    `json:"spec" yaml:"spec"`
}

YAML with gopkg.in/yaml.v3

import "gopkg.in/yaml.v3"

// Same patterns as JSON
data, err := os.ReadFile("deployment.yaml")
if err != nil { ... }

var config Config
if err := yaml.Unmarshal(data, &config); err != nil {
    return fmt.Errorf("parsing YAML: %w", err)
}
3 · Fill the gaps
Parse a manifest into a typed struct — tags map the keys, Unmarshal fills it in place.
type Deployment struct {
    APIVersion string `yaml:`
    Kind       string `yaml:"kind"`
    Replicas   int    `yaml:"replicas"`
}

data, err := os.ReadFile("deployment.yaml") if err != nil { return fmt.Errorf("reading: %w", err) }

var dep Deployment if err := yaml.Unmarshal(data, ); err != nil { return fmt.Errorf("parsing YAML: ", err) }

Handling Unknown Fields

K8s manifests have many fields you might not care about:

// Option 1: use map[string]any for flexible parsing
var raw map[string]any
yaml.Unmarshal(data, &raw)

// Option 2: define only the fields you need — unknown fields are silently ignored
type Minimal struct {
    APIVersion string `yaml:"apiVersion"`
    Kind       string `yaml:"kind"`
}

// Option 3: strict mode — reject unknown fields (json only)
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
4 · From scratch — this feeds your review queue