
Go 的 json.Unmarshal 将所有 JSON 数字(无论是否含小数点)统一解析为 float64,这是由 JSON 规范本身无整数类型决定的,属于标准且预期的行为。
go 的 `json.unmarshal` 将所有 json 数字(无论是否含小数点)统一解析为 `float64`,这是由 json 规范本身无整数类型决定的,属于标准且预期的行为。
在 Go 中处理动态 JSON 数组(如 []interface{})时,开发者常惊讶于 JSON 整数(如 1, 42)被反序列化为 float64 而非 int64。例如:
var args = `[1, 2.5, "aaa", true, false]`
var x []interface{}
json.Unmarshal([]byte(args), &x)
// x[0] 的实际类型是 float64,值为 1.0 —— 即使原始 JSON 写的是整数字面量根本原因在于 JSON 规范:RFC 8259 明确定义 JSON 数字为“一个十进制数字序列,可选包含小数部分和指数部分”,不区分整数与浮点数。因此,Go 的 encoding/json 包为保持语义一致性与实现简洁性,将所有 JSON 数字统一映射为 float64 类型——这既是文档明确声明的行为,也是跨语言 JSON 实现的通用实践(如 Python 的 json.loads() 同样将 1 解析为 float)。
如何安全区分并还原整数?
若业务逻辑需严格区分整数与浮点数(例如参数校验、数据库写入类型匹配),不能依赖 reflect.TypeOf(arg).Kind() == reflect.Int64,而应通过数值特性判断:
for _, arg := range x {
if num, ok := arg.(float64); ok {
// 检查是否为数学意义上的整数(无小数部分)
if num == float64(int64(num)) {
fmt.Printf("integer %d\n", int64(num)) // 安全转换
} else {
fmt.Printf("float %f\n", num)
}
} else if s, ok := arg.(string); ok {
fmt.Printf("string %q\n", s)
} else if b, ok := arg.(bool); ok {
fmt.Printf("bool %t\n", b)
}
}⚠️ 注意事项:
- float64(int64(num)) == num 判断适用于常规整数范围(≤ 2⁵³),超出该范围时 float64 精度不足,可能导致误判;
- 若需精确处理大整数(如 ID、时间戳),推荐使用 json.RawMessage 延迟解析,或定义结构体配合自定义 UnmarshalJSON 方法;
- 避免直接断言 arg.(int64) —— 此操作必然 panic,因 JSON 解析器从不生成 int64。
总结:这不是 bug,而是对 JSON 规范的忠实实现。理解这一设计后,应主动采用数值特征检测(而非类型反射)来识别逻辑整数,并在高精度场景下选用更可控的解析策略。










