go simplejson MustInt() 的使用问题备忘

109 阅读3分钟

simplejson 遇到的问题

1、项目中需要调用别人的接口获取一些信息。因为那接口字段bid在记录不存在的时候会返回int类型,在记录存在的时候会返回string类型。使用了simplejson进行json解析。然后我的预期是返回int,因此使用了MustInt()来处理。但是程序运行的时候发现有时候bid会得到0,抓日志发现对方接口也有返回,不过返回的bid类型是string类型的。(之前的印象中因为MustInt()会把字符类型的数字转为对应的int类型)。

//resp = []byte("{"ret":1,"bid":171023,"tid":1}")
jsonData, err := simplejson.NewJson(resp)
if err != nil {
    fmt.Println("newjsonerrr:", err)
    return 0
}
​
logger.GetLogger(ctx).Info("resp:" + string(resp))
var bid int
if _, exist := jsonData.CheckGet("ret"); exist {
    if jsonData.Get("ret").MustInt() == 1 {
       logger.GetLogger(ctx).Info(fmt.Sprintf("MustInt:%d", jsonData.Get("bid").MustInt()))
       logger.GetLogger(ctx).Info(fmt.Sprintf("MustInt:%s", jsonData.Get("bid").MustString()))
       logger.GetLogger(ctx).Info(fmt.Sprintf("MustInt:%d", cast.ToInt(jsonData.Get("bid").Interface())))
       bid = jsonData.Get("bid").MustInt()
       return bid
    } else {
       logger.GetLogger(ctx).Info("addbar api ret!=1!" + string(resp))
    }
} else {
    logger.GetLogger(ctx).Info("addbar api ret not exist!" + string(resp))
}

就去看了一下MustInt()里面的实现,发现里面有个json.Number类型,

是这么定义的:type Number string、 我错误的以为他和string是一样的类型,认为他应该走第一个case,给我返回int64类型。后面发现虽然Number是基于string定义的,但是他和string类型其实是不一样的类型的。所以如果是一个string类型,MustInt()后会变返回0。经同事提醒,我印象中能把string类型的数字转成int类型的库是cast库。

func (j *Json) Int() (int, error) {
    switch j.data.(type) {
    case json.Number:
       i, err := j.data.(json.Number).Int64()
       return int(i), err
    case float32, float64:
       return int(reflect.ValueOf(j.data).Float()), nil
    case int, int8, int16, int32, int64:
       return int(reflect.ValueOf(j.data).Int()), nil
    case uint, uint8, uint16, uint32, uint64:
       return int(reflect.ValueOf(j.data).Uint()), nil
    }
    return 0, errors.New("invalid value type")
}

于是尝试使用cast库,将数据强制转为int类型,如下:然而还是发现有时候有有时候没有。

cast.ToInt(jsonData.Get("bid").Interface()))

但是toString是一直有值的。

cast.ToString(jsonData.Get("bid").Interface())

看了下cast.ToInt的源码。没有json.NUmber类型的转换。所以还是返回0。

func ToIntE(i interface{}) (int, error) {
    i = indirect(i)
​
    switch s := i.(type) {
    case int:
       return s, nil
    case int64:
       return int(s), nil
    case int32:
       return int(s), nil
    case int16:
       return int(s), nil
    case int8:
       return int(s), nil
    case uint:
       return int(s), nil
    case uint64:
       return int(s), nil
    case uint32:
       return int(s), nil
    case uint16:
       return int(s), nil
    case uint8:
       return int(s), nil
    case float64:
       return int(s), nil
    case float32:
       return int(s), nil
    case string:
       v, err := strconv.ParseInt(s, 0, 0)
       if err == nil {
          return int(v), nil
       }
       return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i)
    case bool:
       if s {
          return 1, nil
       }
       return 0, nil
    case nil:
       return 0, nil
    default:
       return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i)
    }
}

经过断点调试,ToString的时候,类型是fmt.Stringer 也就是任何实现了String()方法的类型都可以

func ToStringE(i interface{}) (string, error) {
    i = indirectToStringerOrError(i)
​
    switch s := i.(type) {
    case string:
       return s, nil
    case bool:
       return strconv.FormatBool(s), nil
    case float64:
       return strconv.FormatFloat(s, 'f', -1, 64), nil
    case float32:
       return strconv.FormatFloat(float64(s), 'f', -1, 32), nil
    case int:
       return strconv.Itoa(s), nil
    case int64:
       return strconv.FormatInt(s, 10), nil
    case int32:
       return strconv.Itoa(int(s)), nil
    case int16:
       return strconv.FormatInt(int64(s), 10), nil
    case int8:
       return strconv.FormatInt(int64(s), 10), nil
    case uint:
       return strconv.FormatUint(uint64(s), 10), nil
    case uint64:
       return strconv.FormatUint(uint64(s), 10), nil
    case uint32:
       return strconv.FormatUint(uint64(s), 10), nil
    case uint16:
       return strconv.FormatUint(uint64(s), 10), nil
    case uint8:
       return strconv.FormatUint(uint64(s), 10), nil
    case []byte:
       return string(s), nil
    case template.HTML:
       return string(s), nil
    case template.URL:
       return string(s), nil
    case template.JS:
       return string(s), nil
    case template.CSS:
       return string(s), nil
    case template.HTMLAttr:
       return string(s), nil
    case nil:
       return "", nil
    case fmt.Stringer:
       return s.String(), nil
    case error:
       return s.Error(), nil
    default:
       return "", fmt.Errorf("unable to cast %#v of type %T to string", i, i)
    }
}

恰好json.Number类型实现了,所以他可以正常转换为String。

image.png

总结:总结:simplejson会将数字类型转换为json.Number类型,

func (j *Json) UnmarshalJSON(p []byte) error {
    dec := json.NewDecoder(bytes.NewBuffer(p))
    dec.UseNumber()
    return dec.Decode(&j.data)
}

MustInt() 不会强转string类型的数字为int。

cast.ToInt()对json.Number类型也转换不了。。。