TOC
问题描述
当外部方法调用以下方法时,返回的结果map[int]int, error
竟然同时为nil。
// GetEvent 查询事件记录
func GetEvent(ctx context.Context, flag string) (map[int]int, error) {
var event = make(map[int]int)
reply, err := redisClient.HGet(ctx, flag, "event")
if err != nil {
return nil, fmt.Errorf("redisClient.HGet flag: %s, err: %w", flag, err)
}
if len(reply) > 0 {
if err = json.Unmarshal([]byte(reply), &event); err != nil {
return nil, fmt.Errorf("json.Unmarshal reply: %s, err: %w", reply, err)
}
}
return event, nil
}
// EventControl 事件控制
func EventControl(ctx context.Context) error {
var flag = "test"
event, err := GetEvent(ctx, flag)
if err != nil {
return fmt.Errorf("getEvent flag: %s, err: %w", flag, err)
}
if event == nil {
// 程序会走到这里吗?
}
return nil
}
问题定位
在 GetEvent 中,return 之前判断了一下event是否为nil,如果在内层就已经变成nil的话,就把内层的所有数据机器人记录了一下,大致如下:
// GetEvent 查询事件记录
func GetEvent(ctx context.Context, flag string) (map[int]int, error) {
var event = make(map[int]int)
reply, err := redisClient.HGet(ctx, flag, "event")
if err != nil {
return nil, fmt.Errorf("redisClient.HGet flag: %s, err: %w", flag, err)
}
if len(reply) > 0 {
if err = json.Unmarshal([]byte(reply), &event); err != nil {
return nil, fmt.Errorf("json.Unmarshal reply: %s, err: %w", reply, err)
}
}
// todo 临时记录
if event == nil {
fmt.Printf("[GetEvent] event is nil, reply: %s, event: %T", reply, event)
}
return event, nil
}
问题解决
通过打印记录发现,的确在GetEvent方法内部event就变成了nil,此时的reply为:”null”。
看下json反序列化的过程发现,”null”反序列化之后会变成对应类型的nil值。
// literalStore decodes a literal stored in item into v.
//
// fromQuoted indicates whether this literal came from unwrapping a
// string from the ",string" struct tag option. this is used only to
// produce more helpful error messages.
func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) error {
......
switch c := item[0]; c {
case 'n': // null
// The main parser checks that only true and false can reach here,
// but if this was a quoted string input, it could be anything.
if fromQuoted && string(item) != "null" {
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
break
}
switch v.Kind() {
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
v.Set(reflect.Zero(v.Type()))
// otherwise, ignore null for primitives/string
}
case 't', 'f': // true, false
value := item[0] == 't'
// The main parser checks that only true and false can reach here,
// but if this was a quoted string input, it could be anything.
if fromQuoted && string(item) != "true" && string(item) != "false" {
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
break
}
switch v.Kind() {
default:
if fromQuoted {
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.readIndex())})
}
case reflect.Bool:
v.SetBool(value)
case reflect.Interface:
if v.NumMethod() == 0 {
v.Set(reflect.ValueOf(value))
} else {
d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.readIndex())})
}
}
......
}
问题总结
redis等第三方库,当获取不存在的记录时,可能不会报错并且返回的结果也不是空字符串”“,而是”null”。
json反序列化”null”,结果将是对应类型的nil值,并且不报错。
To unmarshal JSON into an interface value,
Unmarshal stores one of these in the interface value:
- bool, for JSON booleans
- float64, for JSON numbers
- string, for JSON strings
- []interface{}, for JSON arrays
- map[string]interface{}, for JSON objects
- nil for JSON null
comments powered by Disqus