原因

go-redis 在存储对象数据后,无法通过Scan获取, 这个原因是go-redis对你要Scan的数据类型不支持, 他只支持基本数据类型,time类型包含Duration,*net.IP类型和 实现了go内置encoding包中的BinaryUnmarshaler接口的对象类型

解决方法

自定义结构体类型数据获取

如果存储的是自定义结构体, 在使用Scan获取数据的时候,则必须要实现 encoding.BinaryUnmarshaler 这个接口,否则Scan无法获取数据。 实现解码接口很简单,实际上就是在你的对象上面增加一个UnmarshalBinary方法,当你在调用go-redis的scan的时候他就会自动调用你的自定义对象中的UnmarshalBinary方法,从而你就可以获取到你要的数据了。 方法定义  : UnmarshalBinary(data []byte) error

实现示例:  下面演示的是在自定义的结构体 myStru中实现 BinaryMarshaler, BinaryUnmarshaler接口,其中BinaryMarshaler这个可选,因为在你保存数据时go-redis自动帮你完成了,BinaryMarshaler 这个接口必须要你自己实现,否则无法Scan获取自定义的机构体数据。


type myStru struct {
    Id   string `json:"id"`
    Name string `json:"name"`
}

func (m *myStru) MarshalBinary() (data []byte, err error) {
    return json.Marshal(m)
}

func (m *myStru) UnmarshalBinary(data []byte) error {
    return json.Unmarshal(data, m)
}

map类型的数据获取

 如果你存储的是map类型的数据,你可以直接在scan的时候传递一个 []byte类型的数据,然后在自己json.Unmarshal 一下就获取你要的map数据了, 如

        var userCache map[string]interface{}

        var cval []byte
		err := global.GetRdb().Get(cacheKey, &cval)
		if err != nil {
			global.Log.Error(err.Error())
			return Error(err.Error())
		}
		if err := json.Unmarshal(cval, &userCache); err != nil {
			return Error(err.Error())
		}

go encoding包中的BinaryMarshaler, BinaryUnmarshaler接口定义参考


// BinaryMarshaler is the interface implemented by an object that can
// marshal itself into a binary form.
//
// MarshalBinary encodes the receiver into a binary form and returns the result.
type BinaryMarshaler interface {
	MarshalBinary() (data []byte, err error)
}

// BinaryUnmarshaler is the interface implemented by an object that can
// unmarshal a binary representation of itself.
//
// UnmarshalBinary must be able to decode the form generated by MarshalBinary.
// UnmarshalBinary must copy the data if it wishes to retain the data
// after returning.
type BinaryUnmarshaler interface {
	UnmarshalBinary(data []byte) error
}

go-redis Scan数据获取源码参考


// Scan parses bytes `b` to `v` with appropriate type.
//
//nolint:gocyclo
func Scan(b []byte, v interface{}) error {
	switch v := v.(type) {
	case nil:
		return fmt.Errorf("redis: Scan(nil)")
	case *string:
		*v = util.BytesToString(b)
		return nil
	case *[]byte:
		*v = b
		return nil
	case *int:
		var err error
		*v, err = util.Atoi(b)
		return err
	case *int8:
		n, err := util.ParseInt(b, 10, 8)
		if err != nil {
			return err
		}
		*v = int8(n)
		return nil
	case *int16:
		n, err := util.ParseInt(b, 10, 16)
		if err != nil {
			return err
		}
		*v = int16(n)
		return nil
	case *int32:
		n, err := util.ParseInt(b, 10, 32)
		if err != nil {
			return err
		}
		*v = int32(n)
		return nil
	case *int64:
		n, err := util.ParseInt(b, 10, 64)
		if err != nil {
			return err
		}
		*v = n
		return nil
	case *uint:
		n, err := util.ParseUint(b, 10, 64)
		if err != nil {
			return err
		}
		*v = uint(n)
		return nil
	case *uint8:
		n, err := util.ParseUint(b, 10, 8)
		if err != nil {
			return err
		}
		*v = uint8(n)
		return nil
	case *uint16:
		n, err := util.ParseUint(b, 10, 16)
		if err != nil {
			return err
		}
		*v = uint16(n)
		return nil
	case *uint32:
		n, err := util.ParseUint(b, 10, 32)
		if err != nil {
			return err
		}
		*v = uint32(n)
		return nil
	case *uint64:
		n, err := util.ParseUint(b, 10, 64)
		if err != nil {
			return err
		}
		*v = n
		return nil
	case *float32:
		n, err := util.ParseFloat(b, 32)
		if err != nil {
			return err
		}
		*v = float32(n)
		return err
	case *float64:
		var err error
		*v, err = util.ParseFloat(b, 64)
		return err
	case *bool:
		*v = len(b) == 1 && b[0] == '1'
		return nil
	case *time.Time:
		var err error
		*v, err = time.Parse(time.RFC3339Nano, util.BytesToString(b))
		return err
	case *time.Duration:
		n, err := util.ParseInt(b, 10, 64)
		if err != nil {
			return err
		}
		*v = time.Duration(n)
		return nil
	case encoding.BinaryUnmarshaler:
		return v.UnmarshalBinary(b)
	case *net.IP:
		*v = b
		return nil
	default:
		return fmt.Errorf(
			"redis: can't unmarshal %T (consider implementing BinaryUnmarshaler)", v)
	}
}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐