JSON规范无整数类型,Go的json.Unmarshal将所有数字统一解析为float64是标准行为;处理大整数需用json.Number或string字段配合",string"标签,避免精度丢失。

json.Unmarshal 把所有 JSON 数字都当 float64 是标准行为
这不是 bug,是 Go 对 JSON 规范 RFC 8259 的严格实现。JSON 本身没有整数类型,只定义了“数字”——可以是 123、123.0 或 1.23e+2,统一叫 Number。Go 的 encoding/json 包为简化设计和保证跨语言一致性,把所有 JSON 数字默认解析为 float64。哪怕你传的是 {"id": 42},进 map[string]interface{} 后,id 的实际类型就是 float64,值是 42.0。
interface{} 接收时数字必为 float64,断言 int64 会 panic
当你用 var data map[string]interface{} 接 JSON,然后写 data["id"].(int64),运行时必然 panic:类型断言失败。因为底层根本不是 int64,而是 float64。常见错误写法包括:
-
int(data["id"].(float64))—— 看似能跑,但对大数(>2⁵³)会丢精度 -
int64(data["id"].(float64))—— 同样不安全,且无法处理123.5这类非整数值 - 直接用
reflect.TypeOf判断是否为int64—— 永远返回float64,毫无意义
想保留整数语义?得靠数值特征判断,不是类型反射
如果必须从 []interface{} 或 map[string]interface{} 里识别“逻辑整数”,唯一可靠方式是先断言为 float64,再检查它是否数学上等于某个整数:
if num, ok := val.(float64); ok {
if num == float64(int64(num)) {
// 是整数,可安全转:int64(num)
} else {
// 是浮点数,比如 3.14
}
}
注意:int64(num) 对超大数(如 9223372036854775807)仍可能截断,因为 float64 已经失真。此时该用 json.Number 或结构体 + ,string 标签。
大整数(如 ID、时间戳)必须绕过 float64 中转
一旦数字超过 2^53 - 1(即 9007199254740991),float64 就无法精确表示低比特位。典型表现是前端传 "id": 12345678901234567890,后端收到 12345678901234567168 —— 差错发生在 Unmarshal 阶段,后面怎么转都晚了。
正确做法只有两个方向:
- 前端发字符串,后端字段声明为
string或加json:",string"标签 - 后端启用
Decoder.UseNumber(),字段用json.Number类型,再调.Int64()(注意它会 panic 若含小数点)
别指望 json.Unmarshal 自动猜你是想要 int 还是 float;它只按规范做事。你得主动控制解析路径,否则精度丢失不可逆。











