Implement a custom Variant type that can hold either string or int while satisfying the JSON Marshaler and Unmarshaler interfaces to support correct encoding/decoding of the values.

 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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"strconv"
)

type stringOrInt struct {
	Int    int
	String string
}

func (s *stringOrInt) MarshalJSON() ([]byte, error) {
	if s.Int != 0 {
		return []byte(strconv.Itoa(s.Int)), nil
	}
	if s.String != "" {
		return []byte(fmt.Sprintf(`"%s"`, s.String)), nil
	}

	return nil, nil
}

func (s *stringOrInt) UnmarshalJSON(data []byte) error {
	if len(data) == 0 {
		return nil
	}

	if data[0] == '"' {
		s.String = string(data[1 : len(data)-1])
		return nil
	}

	i64, err := strconv.ParseInt(string(data), 10, 64)
	if err != nil {
		return err
	}
	s.Int = int(i64)
	return nil
}

type envelope struct {
	VariantField *stringOrInt `json:"variant_field,omitempty"`
}

func main() {
	e1 := envelope{&stringOrInt{Int: 42}}
	e2 := envelope{&stringOrInt{String: "4-4-2"}}
	e3 := envelope{&stringOrInt{}}

	j1 := toJSON(e1)
	fmt.Println(j1)
	var e4 envelope
	err := json.Unmarshal([]byte(j1), &e4)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Unmarshal: %+v\n", e4.VariantField)

	j2 := toJSON(e2)
	fmt.Println(j2)
	var e5 envelope
	err = json.Unmarshal([]byte(j2), &e5)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Unmarshal: %+v\n", e5.VariantField)

	fmt.Println(toJSON(e3))
}

func toJSON(v interface{}) string {
	d, _ := json.Marshal(v)
	return string(d)
}