1

I have a constutil package where I have defined some const values using iota.

package constutil

type UserType uint

const (
    Free UserType = iota + 1
    Premium UserType
    ...
)

From json I will get {"user": "Premium", ...}. Now I need to save the value of the user like for Premium it's 2. I tried to get the value like this:

constutil.(req.User)

But it didn't work as req.User returns a string like: "Premium".

I can just do it using map[string]uint. But is there any way of doing it using iota?

Mahmudul Haque
  • 502
  • 2
  • 8
  • 18
  • Not a good idea what you are trying. You should save "Premium" and not 2. Why? Because if you safe 2 and you add a constant to your package maybe 2 will be the value for Free. – apxp Feb 17 '19 at 16:20
  • @apxp In `user definition table` the `ID` of `Premium` user is 2 and I need to save the ID not the value. – Mahmudul Haque Feb 17 '19 at 16:28

2 Answers2

2

Its best to use const string for such Enum use case.

type UserType string

const(
  Premium UserType = "Premium"
)

OR else define custom marshal/unmasrshal:

package main

import (
    "fmt"
    "encoding/json"
)

type UserType uint

func (u UserType) String() string{
    return "Premium"
}

const(
  Premium UserType = 1
)

type User struct{
    T UserType `json:"type"`
}

func (u *User) MarshalJSON() ([]byte, error) {
    return json.Marshal(&struct {
        T string
    }{
        T:    fmt.Sprint(u.T),
    })
}

func (u *User) UnmarshalJSON(data []byte) error {
    aux := &struct {
        T string `json:"type"`
    }{  }
    if err := json.Unmarshal(data, &aux); err != nil {
        return err
    }
    fmt.Println(aux.T)
    switch aux.T{
    case "Premium":
        u.T = Premium
    }
    return nil
}


func main() {
    b := []byte(`{"type":"Premium"}`)
    x := new(User)
    if err := json.Unmarshal(b, x); err != nil{
        fmt.Printf("err: %v\n", err)
    }
    fmt.Printf("unmasrshalled: %v\n", *x)
    fmt.Printf("type: %d\n", x.T)
    b, _ = json.Marshal(x)
    fmt.Printf("marshalled: %v\n", string(b))

}
Saurav Prakash
  • 1,880
  • 1
  • 13
  • 24
2

Don't think there is any built-in way of mapping between iota values and strings. There are some tools that generate code for doing the mapping.

I've been in similar situations and i've done something like this when I didn't want to depend on generators or other tools. Hope it serves as a start for something.

https://play.golang.org/p/MxPL-0FVGMt

package main

import (
    "encoding/json"
    "fmt"
)

type UserType uint

const (
    UserTypeFree UserType = iota
    UserTypePremium
)

var UserTypeToString = map[UserType]string{
    UserTypeFree:    "Free",
    UserTypePremium: "Premium",
}

var UserTypeFromString = map[string]UserType{
    "Free":    UserTypeFree,
    "Premium": UserTypePremium,
}

func (ut UserType) String() string {
    if s, ok := UserTypeToString[ut]; ok {
        return s
    }
    return "unknown"
}

func (ut UserType) MarshalJSON() ([]byte, error) {
    if s, ok := UserTypeToString[ut]; ok {
        return json.Marshal(s)
    }
    return nil, fmt.Errorf("unknown user type %d", ut)
}

func (ut *UserType) UnmarshalJSON(text []byte) error {
    var s string
    if err := json.Unmarshal(text, &s); err != nil {
        return err
    }
    var v UserType
    var ok bool
    if v, ok = UserTypeFromString[s]; !ok {
        return fmt.Errorf("unknown user type %s", s)
    }
    *ut = v
    return nil
}

func main() {
    var ut UserType

    json.Unmarshal([]byte(`"Free"`), &ut)

    fmt.Printf("%#v %v \n", ut, ut)

    b, _ := json.Marshal(ut)

    fmt.Printf("%v\n", string(b))

}
Mattias Wadman
  • 11,172
  • 2
  • 42
  • 57
  • I wouldn't suggest using two maps `UserType -> string` and `string -> UserType`. Rather implement only one and have a `for` loop that extracts key from value. You reduce possible inconsistencies. – ifnotak Feb 17 '19 at 17:56
  • Yes good point. If there is lots of types i would probably generate in some way. Another solution if there is not hundreds of mappings for a type is to use just one map and iterate when mapping in one of the directions instead. My guess is that it will not be your performance bottleneck. – Mattias Wadman Feb 17 '19 at 18:10