DMarlon Studio

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 -.

Go · encoding/json and struct tags in DMarlon Studio: the example's code and its output in the terminal

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.
  • omitempty skips zero values; - always skips the field.
  • Unmarshal needs 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.