json.Unmarshal 默认将所有 JSON 数字解析为 float64,导致超过 2⁵³−1(9007199254740991)的大整数精度不可逆丢失;正确方案是前端发字符串并配合 Go 端 json.Number 或 string 字段加 ",string" 标签。

json.Unmarshal 默认把数字全转成 float64
只要 JSON 里出现 12345678901234567890 这种超过 9007199254740991(即 2^53 - 1)的整数,json.Unmarshal 就会把它塞进 float64——此时精度已不可逆丢失。哪怕结构体字段声明为 int64,也救不回来。
常见错误现象:
-
map[string]interface{}解析后取extra["id"],再强制转int64报panic: interface conversion: interface {} is float64, not int64 - 值看似正常,但最后几位错(比如
7587721483007868979变成7587721483007869000) - 用
json.RawMessage延迟解析,后面仍掉进float64陷阱
结构体字段用 json.Number 显式接收
这是最轻量、最可控的方案:json.Number 本质是字符串别名,只存原始 JSON 字符序列,不触发任何解析。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 结构体字段声明为
json.Number,例如:ID json.Number `json:"id"` - 需要转整数时,调用
.Int64()——它内部做了合法性检查和溢出判断,比strconv.ParseInt(n.String(), 10, 64)更安全 - 如果 ID 可能超
int64范围(比如 128 位雪花 ID),直接用.String()拿原始文本,传给前端或存 DB 都没问题 - 别对
json.Number做加减乘除,它不是数值类型;要算先转,且必须检查返回error
处理 map[string]interface{} 必须全局启用 UseNumber()
如果你没法改结构体定义(比如接第三方 Webhook、泛型数据、动态字段),就得在解码器层面干预。
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 创建
json.Decoder实例后立刻调用.UseNumber(),例如:dec := json.NewDecoder(r); dec.UseNumber() - 这样所有 JSON 数字都会以
json.Number形式存入map[string]interface{},而不是默认的float64 - 后续从
map取值时,先断言为json.Number,再调.Int64()或.String() - 注意:
UseNumber()不影响嵌套的map或[]interface{}内部字段——你得自己递归识别和转换
前端必须发字符串,Go 层才来得及救
Go 用 json.Number 或 string 字段也白搭,如果前端发的是 {"id": 12345678901234567890}(没引号),那这个数字在 JS 序列化时就已经被 JSON.stringify 转成 float64 了,Go 根本收不到原始文本。
前后端必须约定:
- 所有可能超
2^53 - 1的字段,前端 JSON 必须写成字符串:{"id": "12345678901234567890"} - Go 结构体对应字段加
json:",string"标签,例如:ID string `json:"id,string"`——这样 Go 不会尝试解析数字,直接把引号里的内容当字符串收下,零损耗 - 别指望靠“JS 端用
BigInt”解决:HTTP body 仍是 JSON,BigInt无法直接序列化,会报TypeError: Do not know how to serialize a BigInt
最容易被忽略的一点:精度丢失发生在 JSON 解析的第一步,不是 Go 类型转换的问题;一旦进了 float64,就再也拿不回原始数字了。

















