Как использовать строковое значение json для получения значения iota?

У меня есть пакет constutil, в котором я определил некоторые значения const, используя iota.

package constutil

type UserType uint

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

От json я получу {"user": "Premium", ...}. Теперь мне нужно сохранить значение пользователя, например, для Premium это 2. Я попытался получить значение следующим образом:

constutil.(req.User)

Но это не сработало, так как req.User возвращает string вроде: "Premium".

Я могу просто сделать это, используя map[string]uint. Но есть ли способ сделать это с помощью iota?


person Mahmudul Haque    schedule 17.02.2019    source источник
comment
Не очень хорошая идея, что вы пытаетесь. Вы должны сохранить Премиум, а не 2. Почему? Потому что, если вы сохраните 2 и добавите константу в свой пакет, возможно, 2 будет значением для Free.   -  person apxp    schedule 17.02.2019
comment
@apxp В user definition table пользователь ID из Premium равен 2, и мне нужно сохранить идентификатор, а не значение.   -  person Mahmudul Haque    schedule 17.02.2019


Ответы (2)


Не думайте, что существует какой-либо встроенный способ сопоставления между iota значениями и строками. Есть несколько инструментов, которые генерируют код для отображения.

Я был в подобных ситуациях, и я сделал что-то подобное, когда я не хотел зависеть от генераторов или других инструментов. Надеюсь, это послужит началом чего-то.

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

}
person Mattias Wadman    schedule 17.02.2019
comment
Я бы не советовал использовать две карты UserType -> string и string -> UserType. Лучше реализовать только один и иметь цикл for, который извлекает ключ из значения. Вы уменьшаете возможные несоответствия. - person ifnotak; 17.02.2019
comment
Да хороший момент. Если бы было много типов, я бы, вероятно, каким-то образом сгенерировал. Другое решение, если для типа не существует сотен отображений, состоит в том, чтобы использовать только одну карту и выполнять итерацию при отображении в одном из направлений. Я предполагаю, что это не будет вашим узким местом в производительности. - person Mattias Wadman; 17.02.2019

Лучше всего использовать константную строку для такого варианта использования Enum.

type UserType string

const(
  Premium UserType = "Premium"
)

ИЛИ иначе определите пользовательский маршал / демаршал:

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

}
person Saurav Prakash    schedule 17.02.2019