This is a follow up on #348
I'm having the same difficulty with func (*StringStringMapCmd) Result(). It returns map[string]string, which is quite cumbersome to deal with, especially you need to convert string to different types.
I wonder if there is an API like func (*StringStringMapCmd) Scan(interface{}):
type Foo struct {
Key1 string
Key2 int
}
var foo Foo
err := client.HGetAll("key").Scan(&foo)
Pretty much like how json works.
I also think this is a good feature, because I need it.
I also would find this useful. I'm migrating from redigo and am missing the ScanStruct function.
Meanwhile you could use mitchellh/mapstructure
Just curious, what would the HSet/HMSet for this look like?
Hello,
This issue is still relevant to many.
I am using a pipe to HGetAll but cannot find a way to convert the result to a struct.
I tried to work around this by json.Marshal then json.Unmarshal with no luck.
import (
goredis "gopkg.in/redis.v5"
)
c:= goredis.Client()
pipe := c.Pipeline()
pipe.HGetAll("my-key")
cmder, err := pipe.Exec()
if err != nil {
panic(err)
}
for _, cmd := range cmder {
cmd = cmd.(*goredis.StringStringMapCmd).Val() // casts value to `map[string]string`
value := json.Marshal(cmd) // converts the map to `[]byte`
json.Unmarshal([]byte(value), &myStruct)
}
myStruct remains empty.
@vmihailenco How would you suggested to go about this?
Thank you.
UPDATE
I found no way to unamarshal to the struct directly, so i worked around it as follow:
hash := cmd.(*goredis.StringStringMapCmd).Val()
json.Unmarshal([]byte(hash["myKey"]), &myStruct)
I also would find this useful. I'm migrating from redigo and am missing the
ScanStructfunction.Meanwhile you could use mitchellh/mapstructure
Hello, herkyl!this is my test code:
type Article struct {
Title string `json:"title" form:"title"`
Link string `json:"link" form:"link"`
Poster string `json:"poster" form:"poster"`
Time int64 `json:"time" form:"time"`
Votes int `json:"votes" form:"votes"`
}
articleData, err := redisdb.HGetAll(key).Result()
if err != nil {
return err
}
article := new(Article)
err = mapstructure.Decode(articleData, article)
if err != nil {
return err
}
Since the value type returned by the function HGetAll is map[string]string, the mapstructure.Decode encounters a field that is not of type string and will fail to parse.
@sswanv would love to help but I actually stayed on redigo, so I'm not using mapstructure anymore 🤷♂
I am also looking for a feature like this. I moved from Radix as go-redis has better performance but I am definitely missing the ability to unmarshal to structs.
Hey @herkyl, @cmbernard333 and anyone else tackling this thing, I used struct receivers with the same receiver signatures as the commands I need(hmset, hgetall) to tackle this problem... it's kind of like this:
type SomeStruct struct {
StringKey string `json:"string_key"`
LastUpdated time.Time `json:"last_updated"`
}
func (p *SomeStruct) ToMap() (map[string]interface{}, error) {
fieldSet := map[string]interface{}{}
v := reflect.Indirect(reflect.ValueOf(p))
typeOfS := v.Type()
for i := 0; i < v.NumField(); i++ {
fieldName := typeOfS.Field(i).Name
field, ok := typeOfS.FieldByName(fieldName)
hashKey := fieldName
if ok {
if tag := field.Tag.Get("json"); tag != "" {
hashKey = tag
}
}
value := v.Field(i).Interface()
switch valueType := value.(type) {
case string:
if valueType != "" {
fieldSet[hashKey] = value.(string)
}
case time.Time:
if !valueType.IsZero() {
fieldSet[hashKey] = value.(time.Time).Unix()
}
default:
return nil, errors.New(fmt.Sprintf("Unsupported <SomeStruct> type %v for field name %s", reflect.TypeOf(v),
fieldName))
}
}
return fieldSet, nil
}
func (p *SomeStruct) FromMap(fieldMap map[string]string) error {
v := reflect.Indirect(reflect.ValueOf(p))
typeOfS := v.Type()
for i := 0; i < v.NumField(); i++ {
fieldName := typeOfS.Field(i).Name
fieldInterface := v.Field(i).Interface()
field, ok := typeOfS.FieldByName(fieldName)
hashKey := fieldName
if ok {
if tag := field.Tag.Get("json"); tag != "" {
hashKey = tag
}
}
rawValue := ""
if rawValue, ok = fieldMap[hashKey]; ok != true {
rawValue, ok = fieldMap[fieldName]
}
if !ok {
continue
}
switch fieldInterface.(type) {
case string:
v.FieldByName(fieldName).Set(reflect.ValueOf(rawValue))
case time.Time:
timeInt, err := strconv.ParseInt(rawValue, 10, 64)
if err != nil {
return err
}
pTime := time.Unix(timeInt, 0)
v.FieldByName(fieldName).Set(reflect.ValueOf(pTime))
}
}
return nil
}
do note that my actual struct only has string and time attributes but you get the point so you can add the types you need to the type switch
I also would find this useful. I'm migrating from redigo and am missing the
ScanStructfunction.
Meanwhile you could use mitchellh/mapstructureHello, herkyl!this is my test code:
type Article struct { Title string `json:"title" form:"title"` Link string `json:"link" form:"link"` Poster string `json:"poster" form:"poster"` Time int64 `json:"time" form:"time"` Votes int `json:"votes" form:"votes"` } articleData, err := redisdb.HGetAll(key).Result() if err != nil { return err } article := new(Article) err = mapstructure.Decode(articleData, article) if err != nil { return err }Since the value type returned by the function
HGetAllismap[string]string, themapstructure.Decodeencounters a field that is not of type string and will fail to parse.
type Reward struct {
Welfare bool json:"welfare" redis:"w"
Sign bool json:"sign" redis:"s"
}
func Get() (r Reward, err error) {
res, err := m.redis.HGetAll("key").Result()
if err != nil {
return
}
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{TagName: "redis", Result: &r, WeaklyTypedInput: true})
if err != nil {
return
}
return r, decoder.Decode(res)
}
func Set(r Reward) (err error) {
v := make(map[string]interface{}, 0)
objKey := reflect.TypeOf(r)
objVal := reflect.ValueOf(r)
for i := 0; i < objKey.NumField(); i++ {
if tag := objKey.Field(i).Tag.Get("redis"); tag != "" {
v[tag] = objVal.Field(i).Interface()
}
}
res := m.redis.HMSet("key", v)
return res.Err()
}
support tag and weaklyTypedInput
Update: This is now supported. https://github.com/go-redis/redis/blob/f8cbf483f4a193d441fac2cf14be3d84783848c6/example_test.go#L281
Thanks go to @knadh. I've also updated the changelog - https://redis.uptrace.dev/changelog/
Most helpful comment
I also would find this useful. I'm migrating from redigo and am missing the
ScanStructfunction.Meanwhile you could use mitchellh/mapstructure