Go encoding/json.Number Int64 转换失败时怎样区分整数和小数
来源:17golang原创
时间:2026-11-07 18:44:56 278浏览 收藏
json.Number 不是已经转换好的整数,而是 JSON 数字字面量的字符串表示。调用 Int64() 时,如果原文带小数点、使用指数形式、超出 int64 范围,或者本身不是合法整数文本,就会返回错误。区分方法很简单:先用 Number.String() 保留原文,再根据业务语义选择 Int64 或 Float64,不要把转换失败当成零值。
官方文档:https://pkg.go.dev/encoding/json#Number.Int64
Int64适合“必须是整数”的字段;小数和指数形式应该进入浮点或业务自定义分支。对开放结构 JSON,先调用Decoder.UseNumber()保留数字文本,才能在真正知道字段含义后做决定。
先观察 Number 的原始文本
排查 Int64 失败时,第一步不是打印错误字符串,而是看 Number 里到底保存了什么。整数、带小数点的数字、指数形式和越界整数,表面上都属于 JSON number,但它们对整数转换的结果不同。
package main
import (
"encoding/json"
"fmt"
)
func main() {
// 这些值模拟从开放 JSON 字段中保留下来的数字原文。
samples := []json.Number{
"42",
"42.0",
"4e2",
"9223372036854775808",
}
for _, number := range samples {
value, err := number.Int64()
if err != nil {
// 失败时同时保留原文,便于区分格式问题和范围问题。
fmt.Printf("原文=%q Int64失败=%v\n", number.String(), err)
continue
}
fmt.Printf("原文=%q Int64=%d\n", number.String(), value)
}
}
这里的关键点是:json.Number 仍然携带原始文本,Int64 只是一次有明确目标类型的转换尝试。带小数点的 42.0 即使数学上等于整数,也不应默认当作整数通过;指数形式 4e2 也需要由业务决定是否接受。

用 Int64 判断整数边界
如果字段语义是订单数量、版本序号或数据库主键,应该让 Int64 负责最后的边界判断,并把错误原样包回调用方。不要写成“失败就返回 0”,否则缺失值、非法值和真正的零会被混在一起。
package main
import (
"encoding/json"
"fmt"
)
// parseRequiredInt 只接受能完整表示为 int64 的整数文本。
func parseRequiredInt(field string, number json.Number) (int64, error) {
// Int64 同时检查整数格式和 int64 的上下界。
value, err := number.Int64()
if err != nil {
// 包装字段名和原文,方便定位是哪一个 JSON 字段出错。
return 0, fmt.Errorf("字段 %s 的数字 %q 不是有效 int64: %w", field, number.String(), err)
}
return value, nil
}
func main() {
// 这里把错误交给上层,而不是用零值掩盖输入问题。
count, err := parseRequiredInt("count", json.Number("9007199254740993"))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(count)
}
当数字超出 int64 的可表示范围时,即使它没有小数点也会失败。错误消息的具体文本不应该作为业务协议保存;稳定的做法是保留字段名、原始文本和底层错误,把日志或接口错误交给调用方统一映射。
小数和指数形式不要强行走 Int64
如果字段本来就是比例、金额计算中间值或科学计数法输入,可以尝试 Float64()。但这不是“Int64 失败后的通用补救”:浮点数有精度边界,特别大的整数一旦先转成 float64,可能无法保持每一位的精确值。
package main
import (
"encoding/json"
"fmt"
)
// parseRatio 只把明确属于浮点语义的字段转为 float64。
func parseRatio(number json.Number) (float64, error) {
// Float64 允许小数和指数形式,但仍可能返回格式或范围错误。
value, err := number.Float64()
if err != nil {
return 0, fmt.Errorf("比例 %q 不是有效浮点数: %w", number.String(), err)
}
return value, nil
}
func main() {
// 这是比例字段的示例,不应拿同一策略处理主键或计数器。
ratio, err := parseRatio(json.Number("4e-2"))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(ratio)
}
可以把选择规则写成一张小表:
| 输入或字段语义 | 优先方法 | 处理重点 |
|---|---|---|
| 数量、ID、页码 | Int64() | 拒绝小数、指数和越界,并保留错误 |
| 比例、测量值 | Float64() | 接受小数,但说明精度边界 |
| 金额或超大整数 | 保留文本 | 交给 decimal 或业务精确类型处理 |
| 字段结构未知 | UseNumber() | 先保留原文,再按字段语义分流 |
在动态 JSON 中启用 UseNumber
直接把 JSON 解码到 map[string]any 时,数字默认会落到 float64。如果对象字段不固定,又不能提前为每个字段声明结构体,就在解码前调用 UseNumber(),让数字先以 json.Number 进入动态容器。
package main
import (
"bytes"
"encoding/json"
"fmt"
)
// decodeCount 从开放 JSON 中读取一个必须为整数的字段。
func decodeCount(payload []byte) (int64, error) {
// UseNumber 防止数字在进入 any 时先被转成 float64。
decoder := json.NewDecoder(bytes.NewReader(payload))
decoder.UseNumber()
var document map[string]any
if err := decoder.Decode(&document); err != nil {
// 解码错误与数字转换错误分开返回,便于调用方分类。
return 0, fmt.Errorf("JSON 解码失败: %w", err)
}
raw, ok := document["count"]
if !ok {
return 0, fmt.Errorf("缺少 count 字段")
}
number, ok := raw.(json.Number)
if !ok {
return 0, fmt.Errorf("count 不是 json.Number")
}
// 只有到这里,业务才决定 count 必须是 int64。
value, err := number.Int64()
if err != nil {
return 0, fmt.Errorf("count=%q 不是有效整数: %w", number.String(), err)
}
return value, nil
}
func main() {
// 输入内容仅用于展示分支,不代表本文在本机执行过的运行截图。
value, err := decodeCount([]byte(`{"count": 42}`))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(value)
}
UseNumber 只改变动态解码时的暂存类型,不会替你判断字段到底应该是整数还是小数。真正的转换仍然发生在 Int64 或 Float64 调用处,所以错误处理必须留在那里。

把错误分支固定成可回归的规则
最后可以为解析函数覆盖四类输入:纯整数、小数、指数形式和超出范围的整数。测试不必依赖错误字符串,只需判断成功/失败和错误类别;业务日志则记录原文,便于定位上游发送了什么。
package main
import (
"encoding/json"
"fmt"
)
func checkInteger(number json.Number) error {
// 统一入口让所有整数型字段使用同一套边界规则。
_, err := number.Int64()
if err != nil {
return fmt.Errorf("整数转换失败,原文=%q: %w", number.String(), err)
}
return nil
}
func main() {
// 回归样例覆盖成功、语义不符和范围溢出三种主要边界。
cases := []json.Number{"0", "-7", "3.14", "9223372036854775808"}
for _, number := range cases {
fmt.Printf("%q -> %v\n", number.String(), checkInteger(number))
}
}
这样定位 Int64 失败就有了稳定顺序:先看 String() 的原文,再看字段语义,最后选择整数、浮点或精确文本路径。不要把所有数字都强制转成 float64,也不要用零值掩盖解析错误。
常见问题
42.0 为什么不能直接当成 int64?
它的 JSON 文本包含小数点,已经表达了浮点形式。若业务确实允许“数值上等于整数”的小数,应先按业务规则解析并明确校验,而不是依赖 Int64() 替你做隐式取整。
使用 UseNumber() 后还需要判断类型吗?
需要。它让动态容器中的数字变成 json.Number,但字段可能缺失、可能是字符串,也可能不符合整数语义,调用方仍应检查类型和转换错误。
超大整数应该改用 Float64 吗?
不建议。浮点表示可能丢失整数精度;如果每一位都重要,应保留原始文本,再交给精确十进制或专用大整数方案。
-
263 收藏
-
476 收藏
-
198 收藏
-
Golang · Go教程 | 1个月前 | Go教程 · encoding/json · Go encoding/json json.RawMessage json.Decoder 未知字段 兼容升级468 收藏
-
411 收藏
-
471 收藏
-
110 收藏
-
347 收藏
-
138 收藏
-
307 收藏
-
152 收藏
-
466 收藏
-
195 收藏
-
438 收藏
-
314 收藏
-
416 收藏
-
408 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习