json.number 是 go 标准库中底层为 string 的类型别名,用于原样保存 json 数字字面量,解决 float64 解析导致的整数精度丢失问题(如金融 id、大整数),需显式调用 int64()/float64() 转换且注意错误处理。

json.Number 是什么,它能解决什么问题
json.Number 是 Go 标准库 encoding/json 中的一个类型别名,底层是 string。它不参与数值计算,只负责“原样存下 JSON 里的数字字面量”。当你需要避免 float64 解析导致的精度丢失(比如处理 17 位以上整数、金融 ID、时间戳),或者想延迟决定用 int64 还是 float64 解析时,json.Number 就是那个中间缓冲层。
常见错误现象:json.Unmarshal 直接解析到 float64 字段,结果 9223372036854775807(int64 最大值)变成 9223372036854776000 —— 因为 float64 有效整数位只有约 15–16 位。
它不是万能的:不能直接做算术,不能当 int 或 float64 用,必须显式转换;也不影响序列化行为(json.Marshal 对 json.Number 仍输出数字,不是字符串)。
如何在 struct 中正确使用 json.Number
把字段声明为 json.Number 类型,并确保 JSON tag 存在(哪怕为空)。Go 会自动用字符串方式读取数字,跳过 float64 解析环节。
type Order struct {
ID json.Number `json:"id"`
Amount json.Number `json:"amount"`
Metadata map[string]any `json:"metadata"`
}
注意点:
-
json.Number字段不能是 nil 指针,必须是值类型(*json.Number会导致解析失败或空字符串) - 如果字段可能缺失,用
json.RawMessage或自定义 UnmarshalJSON 更稳妥,json.Number对 null 值会解析成空字符串"",后续Int64()会报错 - 不要混用:同一个 struct 里既有
int64又有json.Number字段去接收同一类数字,容易误用未转换的字段
怎么安全地转成 int64 / float64
json.Number 提供 Int64() 和 Float64() 方法,但它们都可能 panic。实际使用必须检查错误:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
n := json.Number("12345678901234567890")
if i, err := n.Int64(); err == nil {
// 安全使用 i
} else {
// 处理溢出或非整数,比如尝试 Float64() 或记录日志
}
<p>f, _ := n.Float64() // 注意:Float64() 不会 panic,但可能丢失精度</p>
关键细节:
-
Int64()要求字符串严格是十进制整数(不能带小数点、指数、前导 +),否则返回 error -
Float64()支持科学计数法和小数,但一旦数字超过 2⁵³ 就无法精确表示整数 - 不要用
strconv.ParseInt(n.String(), 10, 64)替代n.Int64(),因为n.String()可能含空格或非法字符(虽然标准库一般不会,但不保证)
什么时候不该用 json.Number
当你的数据源确定是小整数(int64 或 float64 更简洁。滥用 json.Number 会增加转换开销和代码噪音。
典型误用场景:
- 把整个 API 响应都塞进
map[string]json.Number,结果后续每个字段都要手动转,还漏判了 null - 在高性能循环中频繁调用
Int64()而不缓存结果,触发重复字符串解析 - 用
json.Number接收用户输入后不做范围校验,直接传给数据库,可能被恶意超长数字打爆内存
json.Number 的价值不在“通用”,而在“可控”——你清楚知道哪几个字段必须保精度,就只在那几个地方用,其余照常。它是个手术刀,不是瑞士军刀。










