Lesson 17 · 17 of 20
encoding/json and struct tags · Go
Turn structs into JSON and back in Go with json.Marshal and json.Unmarshal, and control key names with tags such as omitempty and -.

The `encoding/json` package turns structs into JSON and JSON into structs. It only looks at exported fields (those starting with a capital letter), and each field's name in the JSON comes from its tag: the text in backquotes, such as json:"sku". Without a tag the field name would be used as it is, SKU.
Two very useful tag options: `omitempty` leaves the field out when it holds the zero value (which is why the first order has no note) and `-` never writes it, ideal for internal data such as the Margin. Nested structs and slices (Items) are converted on their own.
`json.Marshal(out)` returns a []byte and an error; string(data) turns it into text for printing. Fields come out in the order of the struct.
`json.Unmarshal` does the opposite and needs a pointer, &in, so it can fill the struct. Keys the struct does not have, such as coupon, are ignored without an error, and missing ones leave the zero value. Keys are matched case-insensitively.
If a type does not fit, "nine" where an int is expected, Unmarshal returns an error saying exactly which field failed. `json.MarshalIndent` writes indented JSON that is easier to read: the second argument is a prefix for every line and the third is the indent.
Code · main.go
package mainimport ( "encoding/json" "fmt")type Item struct { SKU string `json:"sku"` Qty int `json:"qty"`}type Order struct { ID int `json:"id"` Customer string `json:"customer"` Items []Item `json:"items"` Note string `json:"note,omitempty"` // left out when empty Margin int `json:"-"` // never encoded}func main() { out := Order{ID: 7, Customer: "Ana Ruiz", Items: []Item{{"KB-01", 1}, {"MS-02", 2}}, Margin: 30} data, err := json.Marshal(out) fmt.Println(string(data), err) input := `{"id": 8, "customer": "Luis Gil", "items": [{"sku": "CB-03", "qty": 4}], "note": "gift", "coupon": "X"}` var in Order err = json.Unmarshal([]byte(input), &in) // pass a pointer so it can fill the struct fmt.Printf("%+v %v\n", in, err) // unknown keys such as coupon are ignored err = json.Unmarshal([]byte(`{"id": "nine"}`), &in) fmt.Println("error:", err) pretty, _ := json.MarshalIndent(in.Items[0], "", " ") fmt.Println(string(pretty))}Output · go run main.go
{"id":7,"customer":"Ana Ruiz","items":[{"sku":"KB-01","qty":1},{"sku":"MS-02","qty":2}]} <nil>
{ID:8 Customer:Luis Gil Items:[{SKU:CB-03 Qty:4}] Note:gift Margin:0} <nil>
error: json: cannot unmarshal string into Go struct field Order.id of type int
{
"sku": "CB-03",
"qty": 4
}Key points
- Only exported (capitalised) fields are encoded.
json:"name"sets the key name.omitemptyskips zero values;-always skips the field.Unmarshalneeds a pointer and returns an error if a type does not fit.- Unknown keys are ignored.
Exercise
Add a Paid bool field with the tag json:"paid" to Order, set it to true in the first order and add "paid": false to the input JSON. Check what each Println prints.