Go的encoding/json包将JSON大整数解析为float64是设计选择,因反序列化到interface{}时默认用float64表示所有数字,易致精度丢失或科学计数法;推荐使用json.Decoder.UseNumber()配合json.Number类型保留原始字符串形式,再按需转Int64()或String()。

json.Unmarshal 为什么把大整数变成 float64?
因为 encoding/json 包在反序列化到 interface{}(比如 map[string]interface{})时,默认把所有 JSON 数字都解析成 float64 —— 这不是 bug,是设计选择。哪怕原始 JSON 是 "id": 9223372036854775807,进内存后也成了 float64,再转回 JSON 可能变科学计数法,或丢失低 3 位精度。
实操建议:
- 别直接用
map[string]interface{}接未知结构的 JSON,除非你后续只读字符串、布尔、嵌套对象,不碰数字 - 若必须用
interface{},立刻启用UseNumber(),让数字先以json.Number字符串形式暂存 -
json.Number是字符串类型,可安全调用.Int64()或.Float64(),不会因底层类型错乱而 panic
用 json.Decoder.UseNumber() 保留原始数字字符串
这是最轻量、最通用的精度保全手段,尤其适合处理第三方 API 返回的“结构不定但数字不能错”的 JSON(如区块链交易 ID、用户 UID、时间戳 ms 级整数)。
实操建议:
- 创建
json.Decoder后立即调用.UseNumber(),顺序不能颠倒 - 解码目标仍用
map[string]interface{},但其中数字字段的值类型变为json.Number,不是float64 - 从
json.Number提取整数时,优先用.Int64();若不确定是否超 int64 范围,改用.String()再交由高精度库处理
dec := json.NewDecoder(r)
dec.UseNumber() // 必须在这一步设
var data map[string]interface{}
if err := dec.Decode(&data); err != nil {
// ...
}
idNum, ok := data["id"].(json.Number)
if ok {
id, _ := idNum.Int64() // 安全转换
}
struct 字段声明为 int64 比 interface{} 更可靠
如果你知道 JSON 结构,就别绕弯子——直接定义 struct,把数字字段明确写成 int64 或 uint64。这时 json.Unmarshal 会跳过 float64 中间态,直解析为整型。
Node.js 可靠编程技能。避免事件循环阻塞、异步陷阱、ESM 兼容问题和内存泄漏。触发词:Node.js 开发、async/await、流处理、性能优化、安全编码。
实操建议:
- 字段类型必须匹配:JSON 里是整数,Go 里就得是
int、int64、uint64等,不能是float64或string - 如果 JSON 数字可能溢出
int64(比如大于9223372036854775807),要么用string存原始文本,要么用专用类型如github.com/shopspring/decimal.Decimal - 注意:即使用了
int64,若 JSON 字段缺失或为null,该字段会是零值;需配合omitempty或指针字段(*int64)区分“未提供”和“值为 0”
json.Number.String() 不等于原始 JSON 字符串
json.Number 的 .String() 方法返回的是规范化的数字字符串:去除了前导零、合并了指数符号(如 "1e6" → "1000000"),但不会补小数点后零("1.0" → "1")。它保证数值等价,不保证字面一致。
这意味着:
- 你无法靠
json.Number.String()还原原始 JSON 的格式(比如保留"000123"或"1.000") - 若业务强依赖原始字面(如校验签名、比对哈希输入),必须用自定义
UnmarshalJSON方法,或全程走json.RawMessage -
json.Number是只读的,不能修改;也不能直接参与算术运算,必须先转成数值类型
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










