Convert iota List to String in JSON

· 389 words · 2 minutes read

Constants in Go, when combined with iota, can be a nice way to use options and settings as a list. We have an example below using genders to show this in action. The problem comes if you want to encode/decode structs to JSON, then the constant will be encoded into an int - sometimes it would be nice to convert this to a string to make it easier to represent what it is.

The Problem:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
	"encoding/json"
	"fmt"
)

const (
	GenderNotSet = iota
	GenderMale
	GenderFemale
	GenderOther
)

type Human struct {
	Gender int `json:"gender"`
}

func main() {
	me := Human{
		Gender: GenderMale,
	}
	prettyJSON, _ := json.MarshalIndent(me, "", "    ")
	fmt.Println(string(prettyJSON))
}

iota example on encoding to json

The Solution:

The solution is to use the same constant list, but create your own struct for this type. This is so we can add the MarshalJSON and UnmarshalJSON methods to it. These in turn then use maps to convert the constant into/from a string.

To make the example easier to read, we’ve split this into two files.

gender.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package main

import (
	"bytes"
	"encoding/json"
)

type Gender int

const (
	GenderNotSet = iota
	GenderMale
	GenderFemale
	GenderOther
)

var toString = map[Gender]string{
	GenderNotSet: "Not Set",
	GenderMale:   "Male",
	GenderFemale: "Female",
	GenderOther:  "Other",
}

var toID = map[string]Gender{
	"Not Set": GenderNotSet,
	"Male":    GenderMale,
	"Female":  GenderFemale,
	"Other":   GenderOther,
}

func (g Gender) MarshalJSON() ([]byte, error) {
	buffer := bytes.NewBufferString(`"`)
	buffer.WriteString(toString[g])
	buffer.WriteString(`"`)
	return buffer.Bytes(), nil
}

func (g *Gender) UnmarshalJSON(b []byte) error {
	var j string
	if err := json.Unmarshal(b, &j); err != nil {
		return err
	}
	*g = toID[j]
	return nil
}

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

import (
	"encoding/json"
	"fmt"
)

type Human struct {
	Gender Gender `json:"gender"`
}

func main() {
	me := Human{
		Gender: GenderMale,
	}
	prettyJSON, _ := json.MarshalIndent(me, "", "    ")
	fmt.Println(string(prettyJSON))
}

iota example on encoding to json after using struct

Image of Author Edd Turtle

Author:  Edd Turtle

Edd is the Lead Developer at Hoowla, a prop-tech startup, where he spends much of his time working on production-ready Go and PHP code. He loves coding, but also enjoys cycling and camping in his spare time.

See something which isn't right? You can contribute to this page on GitHub or just let us know in the comments below - Thanks for reading!