一次偶然的情况, 使用encoding/jsonUnmarshal 处理 int 类型字段时会被处理成 float64,且使用科学记数法展示

我们先组一个数据:

1
2
3
4
5
6
{
    "uid": 14625461,
    "ad_type": 21,
    "sys": 1,
    "hospital_id": "108720"
}

直接使用 Unmarshal 处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// JsonDecode 通用的 json 解析方法
func JsonDecode(raw string, tpl interface{}, useNumber bool) error {
	if useNumber {
		d := json.NewDecoder(bytes.NewReader([]byte(raw)))
		d.UseNumber()
		return d.Decode(tpl)
	}

	return json.Unmarshal([]byte(raw), &tpl)
}


func TestDemo(t *testing.T) {
	t.Log("start")
	data := `
{
    "uid": 14625461,
    "ad_type": 21,
    "sys": 1,
    "hospital_id": "108720"
}
`
	tpl := map[string]interface{}{}
	JsonDecode(data, &tpl, false)
	t.Logf("%#+v \n", tpl)
}

这时候输出:

1
map[string]interface {}{"ad_type":21, "hospital_id":"108720", "sys":1, "uid":1.4625461e+07}

本来应该正常显示的 uid 解析后的值变成了 1.4625461e+07, 这种数据格式明细不符合我们的预期, 因此需要特殊处理一下, 按上面的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// JsonDecode 通用的 json 解析方法
func JsonDecode(raw string, tpl interface{}, useNumber bool) error {
	if useNumber {
		d := json.NewDecoder(bytes.NewReader([]byte(raw)))
		d.UseNumber()
		return d.Decode(tpl)
	}

	return json.Unmarshal([]byte(raw), &tpl)
}


func TestDemo(t *testing.T) {
	t.Log("start")
	data := `
{
    "uid": 14625461,
    "ad_type": 21,
    "sys": 1,
    "hospital_id": "108720"
}
`
	tpl := map[string]interface{}{}
	JsonDecode(data, &tpl, true)
	t.Logf("%#+v \n", tpl)
}

此时输出:

1
map[string]interface {}{"ad_type":"21", "hospital_id":"108720", "sys":"1", "uid":"14625461"}

关键代码就在:

1
2
3
4
5
	d := json.NewDecoder(bytes.NewReader([]byte(raw)))
    // UseNumber causes the Decoder to unmarshal a number into an interface{} as a
    // Number instead of as a float64.
    d.UseNumber() // 这里是关键
    return d.Decode(tpl)

这里再追一下上层代码, 发现到这个函数里面:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// convertNumber converts the number literal s to a float64 or a Number
// depending on the setting of d.useNumber.
func (d *decodeState) convertNumber(s string) (interface{}, error) {
	if d.useNumber {
		return Number(s), nil
	}
	f, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return nil, &UnmarshalTypeError{Value: "number " + s, Type: reflect.TypeOf(0.0), Offset: int64(d.off)}
	}
	return f, nil
}

这里如果不设置d.UseNumber()将会把数字类型转换成 float64 格式的, 如果设置了, 就变成了 json.Number格式的, 底层是 string 类型

基于这个情况,在处理 json 数据时需要多一层考虑,使用 str := rawValue.(json.Number).String()interface{} 的转成 string 类型, 然后再考虑是否转成 int 类型, 代码如下:

1
2
	str := rawValue.(json.Number).String()
	intValue, err = strconv.Atoi(str)