当前位置:首页 > 文章列表 > Golang > Go问答 > Go JSON 数字解码到 int 为什么会溢出

Go JSON 数字解码到 int 为什么会溢出

来源:17golang原创 2026-10-14 22:41:49 0浏览 收藏

我在接一个外部 JSON 接口时遇到过一个很容易误判的问题:请求整体看起来是合法 JSON,字段名也能对应上,但把一个数字解码到 Go 的整数类型时,返回了 json: cannot unmarshal number ... into Go struct field ...。这不是 JSON 语法坏了,而是数字已经越过了目标 Go 类型的表示范围。

先记住结论:JSON 数字解码到 int 发生溢出时,encoding/json 会返回 *json.UnmarshalTypeError,不会替你截断、取模或自动改用更宽的整数。字段可能保持零值,其他字段也只是“尽力”继续解码,不能把返回错误当成提示信息而忽略。

JSON 数字超过 Go 整数目标类型范围并形成 UnmarshalTypeError 的静态结构图
图1:JSON 数字、Go 整数范围与 UnmarshalTypeError 的边界关系说明图,不是运行截图。

先确认溢出发生在哪个类型边界

JSON 规范允许数字以数字字面量出现,但它没有替 Go 代码决定最终要放进 int、int64 还是字符串。真正写入结构体字段时,encoding/json 必须检查目标类型能否容纳这个值。

这里还有一个容易踩的点:int 的宽度跟平台有关。64 位 Go 环境通常可以表示 64 位有符号整数,32 位环境的 int 范围则更小。因此,用 2147483648 演示时,在 int32 上一定越界,但在 64 位 int 上未必越界;要测试 64 位 int,可以使用超过 int64 上限的数字。

package main

import (
	"encoding/json"
	"fmt"
)

type Payload struct {
	Count int32  `json:"count"` // 用固定宽度类型,让示例边界不受平台影响
	Name  string `json:"name"`  // 观察数字出错时其它字段的解码结果
}

func main() {
	data := []byte(`{"count":2147483648,"name":"demo"}`)
	var p Payload
	if err := json.Unmarshal(data, &p); err != nil {
		// 数值超出 int32 范围时,标准库返回类型错误而不是自动截断
		fmt.Printf("decode failed: %v\n", err)
	}
	fmt.Printf("payload: %+v\n", p)
}

这个例子里,问题在“目标类型范围”而不是字段名或 JSON 逗号。生产代码应把错误当成一次解码失败处理;不要因为 Name 仍然被读出来,就认为整个对象已经可靠。

从 UnmarshalTypeError 取出具体字段

只打印 err.Error() 能看到一行描述,但接口层通常还需要知道是哪个 JSON 值、哪个 Go 类型和哪个字段出错。官方类型 UnmarshalTypeError 提供了 Value、Type、Offset、Struct 和 Field 等定位信息,可以通过 errors.As 提取。

package main

import (
	"encoding/json"
	"errors"
	"fmt"
)

type Payload struct {
	Count int32 `json:"count"` // 目标类型故意设为固定宽度,便于定位范围错误
}

func decode(data []byte) error {
	var p Payload
	if err := json.Unmarshal(data, &p); err != nil {
		var typeErr *json.UnmarshalTypeError
		if errors.As(err, &typeErr) {
			// 把字段路径和目标类型写入业务日志,方便调用方修正协议
			return fmt.Errorf("field %s received %s for %s: %w", typeErr.Field, typeErr.Value, typeErr.Type, err)
		}
		// 语法错误等其它解码失败继续原样返回
		return err
	}
	return nil
}

UnmarshalTypeError 只说明“某个 JSON 值不适合某个 Go 类型”,它不是一个自动修复器。对外接口可以把它转换成明确的参数错误;对内部消费则应保留原始错误和字段路径,避免把协议问题伪装成默认值。

字段型数据怎样避免直接塞进 int

如果字段的协议含义是数量、金额分、数据库主键或外部系统编号,先问清楚它的最大范围和是否允许前导零。只要协议范围稳定,优先在结构体上表达真实边界,而不是把所有数字都写成 int。

  • 明确不超过 32 位时使用 int32,让范围约束可读、可移植。
  • 需要覆盖 64 位有符号整数时使用 int64,并在解码后继续做业务范围检查。
  • 编号可能超过 64 位,或不能丢失前导零时使用 string,把它当标识符而不是算术量。
  • 协议范围不可信时先接收为字符串或 json.Number,确认边界后再转成业务类型。
type Order struct {
	// 外部订单号只用于匹配,不参与加减,使用字符串避免精度和前导零问题
	OrderID string `json:"order_id"`
	// 金额分需要明确 64 位范围,解码成功后仍要检查业务上限
	AmountCents int64 `json:"amount_cents"`
}

如果只能修改一处,先修改协议模型和错误处理,而不是在收到错误后把字段强行改成 float64。后一种做法很可能把“解码失败”换成“业务数值悄悄变形”。

动态 JSON 怎样保留大数字

当 JSON 的字段结构运行时才知道,常见写法是解码到 map[string]any。但默认情况下,接口里的 JSON 数字会被解码成 float64;对大整数来说,这会带来整数精度问题。此时可以使用 Decoder.UseNumber,让数字先保留成 json.Number,再决定是否调用 Int64。

package main

import (
	"encoding/json"
	"fmt"
	"strings"
)

func main() {
	data := `{"id":9223372036854775807}`
	dec := json.NewDecoder(strings.NewReader(data))
	dec.UseNumber() // 动态对象中的数字先保留原始文本,不直接变成 float64

	var v map[string]any
	if err := dec.Decode(&v); err != nil {
		panic(err) // 示例中输入来自受控字符串,服务代码应返回错误
	}

	n, ok := v["id"].(json.Number)
	if !ok {
		panic("id is not a JSON number") // 先确认动态字段的实际类型
	}
	id, err := n.Int64()
	if err != nil {
		panic(err) // 超出 int64 时在这里失败,不把它静默转成近似值
	}
	fmt.Println(id)
}
字段型和动态 JSON 处理大数字的两条安全分支静态说明图
图2:字段型模型与动态 JSON 分别选择宽类型、字符串或 UseNumber 的处理分支说明图,不是运行截图。

UseNumber 解决的是“先不要把动态数字变成 float64”,并不意味着任何数字都能转成 int64。转换仍然是一个明确的边界动作,失败时要把错误交给调用方或业务校验层。

哪些修复方式容易掩盖问题

第一,忽略 json.Unmarshal 的返回值。标准库可能尽力处理剩余字段,但这不等于对象满足协议;继续入库或下单会把坏数据带到更晚的阶段。

第二,把所有数字改成 float64。它适合需要小数的测量值,却不适合作为大整数编号或精确金额的默认容器。

第三,只在 64 位本机测试 int。部署到不同架构、跨语言传递或接收更大的外部编号时,平台依赖会重新暴露。对协议字段,固定宽度类型、字符串和明确的错误分支比“本机能跑”更可靠。

我现在的处理顺序是:先确认 JSON 数字的业务语义,再确认目标类型的范围;结构固定就把范围写进结构体,结构动态就用 UseNumber 延迟决定;最后用 errors.As 记录具体字段。这样 UnmarshalTypeError 是一条清晰的协议边界,而不是上线后才出现的模糊异常。

常见问题

JSON 数字大于 int 的上限,会自动变成最大值吗?

不会。对目标整数类型发生溢出时,encoding/json 返回 UnmarshalTypeError,不应依赖截断或饱和值结果。

能不能先解码到 map[string]any 再处理?

可以,但默认数字会进入 float64。需要保留大整数文本时,先对 Decoder 调用 UseNumber,再显式选择 Int64 或字符串方案。

出现 UnmarshalTypeError 后,其它字段是否可信?

不能一概而论。官方文档说明解码会尽力继续,但出错字段会被跳过,错误字段之后的字段是否已写入也没有保证。把整个解码结果视为失败更安全。

总结:Go JSON 数字解码到 int 溢出,本质是输入值与目标类型范围不匹配。固定结构优先用准确的整数宽度或字符串,动态结构先用 UseNumber 保留数字,再通过显式转换和 UnmarshalTypeError 把问题定位到字段。

官方资料:https://pkg.go.dev/encoding/json

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go http.Request.SetPathValue 怎样在测试中注入路径变量Go http.Request.SetPathValue 怎样在测试中注入路径变量
上一篇
Go http.Request.SetPathValue 怎样在测试中注入路径变量
Go 测试中 ticker 未停止为何让 goroutine 泄漏
下一篇
Go 测试中 ticker 未停止为何让 goroutine 泄漏
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    474次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    531次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    551次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    503次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    323次使用