Go JSON 数字被双引号包住时怎么映射到整数
接口返回 {"user_id":"123"},而 Go 结构体写成 UserID int,直接反序列化会报类型不匹配。原因是引号让 123 变成了 JSON 字符串,不再是 JSON 数字。若上游始终返回这种格式,给字段加 json:",string" 是最小改动;若上游有时返回 123、有时返回 "123",则应定义自定义类型,明确哪些输入允许转换。
- JSON 的
123和"123"是两种值类型,普通int字段不会替你做字符串转整数。 - 格式固定为数字字符串时,用
json:",string"直接映射;它不是“忽略所有非数字字符”的宽松开关。 - 格式混用时,在
UnmarshalJSON中集中处理null、小数、空白和溢出,避免业务代码到处转换。
为什么 "123" 不是 Go 的 JSON number
encoding/json 反序列化时会区分 JSON number、JSON string、布尔值和 null。因此下面两个字段的视觉内容相近,类型却不同:
{"count":123}
{"count":"123"}
目标字段若是普通 int,第一行可以按整数解码,第二行则会返回 json.UnmarshalTypeError。这个错误说明输入值和目标 Go 类型不匹配,不是 JSON 语法错误,也不是把字段名改成大写就能解决。先确认上游是否真的把数字序列化成了字符串,再决定接收策略。

固定返回数字字符串时使用 json:",string"
如果接口契约明确规定 count 始终是包在字符串里的整数,可以在字段标签中声明 string 选项。它的含义是:这个数值字段在 JSON 中以字符串承载,标准库负责解开这一层编码。
package main
import (
"encoding/json"
"fmt"
)
type Payload struct {
// 上游固定返回 "123" 时,string 选项负责解除 JSON 字符串包装。
Count int `json:"count,string"`
}
func main() {
data := []byte(`{"count":"123"}`)
var payload Payload
if err := json.Unmarshal(data, &payload); err != nil {
// 解析失败时不要继续使用可能不完整的请求对象。
panic(err)
}
fmt.Println(payload.Count)
}
这种写法适合“格式稳定、字段数量不多”的 DTO。它不会把 "12.5" 自动四舍五入成整数,也不应被用来掩盖上游字段契约漂移。若返回的是普通 JSON 数字 123,却把标签写成 json:",string",也要按接口约定重新检查,而不是继续堆兼容分支。
| JSON 输入 | 字段定义 | 结果 | 适用判断 |
|---|---|---|---|
123 | int | 整数 | 标准 JSON number |
"123" | int | 类型错误 | 缺少字符串数值声明 |
"123" | int `json:",string"` | 整数 | 固定数字字符串 |
"12.5" | 整数方案 | 应拒绝 | 不要隐式截断 |
混合格式要把兼容策略放进自定义类型
有些历史接口在不同版本中返回过 123 和 "123"。此时只加标签不够,因为标签表达的是一种稳定契约。可以定义 FlexibleInt,在一个地方接收允许的输入,再交给 strconv.ParseInt 做范围检查:
type FlexibleInt int64
func (n *FlexibleInt) UnmarshalJSON(data []byte) error {
if string(data) == "null" {
// 是否把 null 视为缺省值,要与接口契约保持一致。
*n = 0
return nil
}
var text string
if err := json.Unmarshal(data, &text); err == nil {
// 字符串路径不做四舍五入,只接受整数文本。
parsed, err := strconv.ParseInt(strings.TrimSpace(text), 10, 64)
if err != nil {
return fmt.Errorf("invalid integer string %q: %w", text, err)
}
*n = FlexibleInt(parsed)
return nil
}
var number json.Number
if err := json.Unmarshal(data, &number); err != nil {
// 其他类型直接返回错误,避免把布尔值当成数字。
return fmt.Errorf("integer must be a number or string: %w", err)
}
parsed, err := strconv.ParseInt(string(number), 10, 64)
if err != nil {
return fmt.Errorf("invalid integer number %q: %w", number, err)
}
*n = FlexibleInt(parsed)
return nil
}
示例的兼容面是刻意收窄的:允许整数数字、整数文本和 null,拒绝小数、布尔值和超出 int64 的值。实际项目还应决定 null 是零值、可选字段还是错误;如果字段必须存在,建议用指针或额外的存在性标记表达,而不要让零值承担两种含义。

上线前用边界样例确认整数没有被悄悄改写
这类字段最容易在“能解析”之后留下更隐蔽的问题。不要只测 "123" 一个成功样例,至少把输入类型和业务政策列出来:
- 格式:是否允许前后空格,是否拒绝小数、科学计数法和空字符串。
- 范围:目标是
int还是int64,服务在 32 位环境上是否仍然安全。 - 空值:
null、缺字段和""是否分别代表缺省、未提供和非法输入。 - 输出:读取后再次
json.Marshal时,要输出普通数字还是继续保留字符串形式。
func TestFlexibleInt(t *testing.T) {
tests := []struct {
name string
input string
want FlexibleInt
ok bool
}{
{"number", `123`, 123, true},
{"string", `"123"`, 123, true},
{"fraction", `"12.5"`, 0, false},
{"boolean", `true`, 0, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var got FlexibleInt
err := json.Unmarshal([]byte(tt.input), &got)
// 成功样例比较值,失败样例只确认错误存在。
if (err == nil) != tt.ok {
t.Fatalf("error = %v, want success = %v", err, tt.ok)
}
if err == nil && got != tt.want {
t.Fatalf("got %d, want %d", got, tt.want)
}
})
}
}
如果只是单个固定字段,优先选结构体标签;如果格式混用或要复用同一规则,使用自定义类型。关键不是“把字符串硬转成整数”,而是把允许的输入集合、位数范围和空值语义写成可复查的代码边界。
常见问题
加了 json:",string" 后为什么普通数字反而不合适?
这个选项表达的是数值被 JSON 字符串包住的格式。普通数字应使用没有该选项的整数字段,混合格式则交给自定义类型。
能不能用 strconv.Atoi 直接转?
可以处理已经拿到的字符串,但它受平台 int 位数影响。需要固定协议范围时,使用 strconv.ParseInt 并指定 bitSize 更清楚。
数字字符串里的空格应该自动去掉吗?
只有接口契约允许时才使用 strings.TrimSpace。若空格代表上游数据错误,应该拒绝并记录原始字段,而不是静默修正。
遇到 Go JSON 数字被双引号包住,先看清楚输入是 123 还是 "123"。固定字符串格式用 json:",string",历史格式混用则把转换策略收口到自定义类型,并用边界测试守住整数范围。
Java List 删除整数时怎么区分下标和元素
- 上一篇
- Java List 删除整数时怎么区分下标和元素
- 下一篇
- Python dataclass 的列表默认值为什么要用 default_factory
-
- Golang · Go问答 | 7分钟前 |
- Go url.Parse 成功是否代表地址可以直接访问
- 388浏览 收藏
-
- Golang · Go问答 | 19分钟前 |
- Go 拼接 URL 为什么不能直接用 filepath.Join
- 387浏览 收藏
-
- Golang · Go问答 | 31分钟前 |
- Go CSV 每行列数不同为什么读取失败
- 221浏览 收藏
-
- Golang · Go问答 | 43分钟前 |
- Go XML 标签有命名空间前缀时为什么匹配不到
- 428浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go JSON 时间字段不是 RFC3339 格式时怎么解析
- 302浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go 接口收到多余 JSON 字段为什么没有报错
- 273浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- Go 空切片转 JSON 为什么有时是 null 有时是空数组
- 153浏览 收藏
-
- Golang · Go问答 | 1小时前 | JSON · go · 编码 · json.Encoder SetEscapeHTML JSON转义
- Go JSON 里的小于号为什么变成 Unicode 转义
- 474浏览 收藏
-
- Golang · Go问答 | 2小时前 | go · zip · 错误排查 · 文件压缩 · Go archive/zip zip.Writer Writer.Close
- Go 生成的 ZIP 无法打开怎么检查 Writer 关闭顺序
- 489浏览 收藏
-
- Golang · Go问答 | 2小时前 |
- Go map 返回零值怎么判断键是否存在
- 298浏览 收藏
-
- Golang · Go问答 | 2小时前 | 并发 · Mutex · go · sync.Mutex 结构体复制 copylocks
- Go 结构体包含 Mutex 时为什么不能直接复制
- 233浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 158次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 87次使用
-
- ClickPrompt
- ClickPrompt是一款专为AI提示词编写者设计的开源在线工具,支持Stable Diffusion绘图、ChatGPT对话及GitHub Copilot代码辅助。提供Prompt自动生成、一键运行、社区分享及可视化优化功能,帮助用户高效获取精准AI输出。
- 46次使用
-
- PromptHero
- PromptHero是专业的AI提示词搜索引擎与优化平台,支持Stable Diffusion、Midjourney等主流模型。提供海量提示词库、分类搜索、在线课程及社区互动,助力用户高效生成高质量AI图像与文本。
- 30次使用
-
- Stable Diffusion Prompt Book
- 深入解析OpenArt推出的Stable Diffusion Prompt Book,这本免费的开源提示词指南涵盖从基础语法到高级技巧,提供风格化词库与参数建议,助您优化AI绘画生成效果。
- 30次使用
-
- 用Nginx反向代理部署go写的网站。
- 2023-01-17 502浏览
-
- GoLand调式动态执行代码
- 2023-01-13 502浏览
-
- Go net.Conn 写入超时为何仍会卡住:SetWriteDeadline、部分写入与连接复用检查
- 2026-08-30 501浏览
-
- Go 问答:httptrace.ClientTrace GotConnInfo 怎么判断连接是否复用:连接池与请求时序边界
- 2026-08-28 501浏览
-
- Go netip.Prefix.Contains 判断网段为什么出错:地址族、掩码长度与规范化
- 2026-08-27 501浏览

