Lesson 19 / 26

JSON With Struct Tags

Marshal and unmarshal safely.

Tags control field names and omission

encoding/json maps exported struct fields to JSON. Struct tags set names (json:"customer"), skip empty values (omitempty), or exclude fields (json:"-"); unexported fields are always ignored. Unknown JSON keys are ignored by default (use a Decoder with DisallowUnknownFields to reject them), and type mismatches return descriptive errors. Validate decoded data before using it.

Tags, ignored fields and a type error, run

I ran this with Go 1.27.1 (go run . in a module named demo, standard library only). Marshalling omits empty Items, the "-" tagged Notes and the unexported field. Unmarshalling ignores the unknown "extra" key; a string for the numeric id returns a clear error.

package main

import (
	"encoding/json"
	"fmt"
)

type Order struct {
	ID       int      `json:"id"`
	Customer string   `json:"customer"`
	Items    []string `json:"items,omitempty"`
	Notes    string   `json:"-"` // never serialised
	internal string   // unexported: ignored by encoding/json
}

func main() {
	o := Order{ID: 7, Customer: "Asha", Notes: "secret", internal: "x"}
	b, _ := json.Marshal(o)
	fmt.Println(string(b))

	var in Order
	err := json.Unmarshal([]byte(`{"id": 8, "customer": "Ravi", "items": ["pen"], "extra": true}`), &in)
	fmt.Printf("%+v err=%v\n", in, err)

	err = json.Unmarshal([]byte(`{"id": "eight"}`), &in)
	fmt.Println("type error:", err)
}

Output:

{"id":7,"customer":"Asha"}
{ID:8 Customer:Ravi Items:[pen] Notes: internal:} err=<nil>
type error: json: cannot unmarshal string into Go struct field Order.id of type int

Reject unknown fields in APIs

For request bodies, a json.Decoder with DisallowUnknownFields catches typos in client payloads.

Quick check: What does the tag json:"-" do?

  • Encrypts it
  • Makes the field required
  • Renames it to "-"
  • Excludes the field from JSON encoding and decoding
Answer

Excludes the field from JSON encoding and decoding — Keep internal fields out of payloads.