Go JSON 数字被双引号包住时怎么映射到整数
来源:17golang原创
时间:2026-09-06 03:00:24 434浏览 收藏
接口返回 {"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",历史格式混用则把转换策略收口到自定义类型,并用边界测试守住整数范围。
-
502 收藏
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
388 收藏
-
387 收藏
-
221 收藏
-
428 收藏
-
302 收藏
-
273 收藏
-
153 收藏
-
496 收藏
-
474 收藏
-
489 收藏
-
298 收藏
-
233 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习