My blog post from yesterday made me think… how can you marshal and unmarshal enums in Go correctly?

So I modified internal/coffee/coffee.go and added a MarshalText() and UnmarshalText() methods:

package coffee

import (
    "fmt"
    "strings"
)

//go:generate stringer -linecomment -type=Coffee

type Coffee int

const (
    Drip       Coffee = iota // drip coffee
    Latte                    // latte
    Breve                    // breve
    Cappuccino               // cappuccino
)

func (c Coffee) MarshalText() ([]byte, error) {
    return []byte(c.String()), nil
}

func (c *Coffee) UnmarshalText(text []byte) error {
    want := string(text)
    for i := 0; i < len(_Coffee_index)-1; i++ {
        name := _Coffee_name[_Coffee_index[i]:_Coffee_index[i+1]]
        if strings.EqualFold(name, want) {
            *c = Coffee(i)
            return nil
        }
    }
    return fmt.Errorf("invalid Coffee %q", want)
}

I then modified main.go to add a new type named Drink which expected a Coffee in its field:

package main

import (
    "encoding/json"
    "fmt"

    "goenumtest/internal/coffee"
)

type Drink struct {
    Type coffee.Coffee `json:"type"`
}

func PrintCoffee(c coffee.Coffee) {
    fmt.Println("Coffee type is:", c)
}

func main() {
    PrintCoffee(coffee.Drip)
    PrintCoffee(coffee.Latte)

    payload := []byte(`{"type":"cappuccino"}`)

    drink := Drink{}
    if err := json.Unmarshal(payload, &drink); err != nil {
        panic(err)
    }

    fmt.Println("Unmarshaled drink is:", drink)

    s, err := json.Marshal(drink)
    if err != nil {
        panic(err)
    }

    fmt.Println("Marshaled drink is:", string(s))
}

This worked great!

Running main.go shows:

$ go run main.go
Coffee type is: drip coffee
Coffee type is: latte
Unmarshaled drink is: {cappuccino}
Marshaled drink is: {"type":"cappuccino"}

I’ve updated my repo with this new code.