当前位置:首页 > 文章列表 > Golang > Go问答 > Go JSON 数字被双引号包住时怎么映射到整数

Go JSON 数字被双引号包住时怎么映射到整数

来源:17golang原创 2026-09-06 03:00:24 0浏览 收藏

接口返回 {"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 语法错误,也不是把字段名改成大写就能解决。先确认上游是否真的把数字序列化成了字符串,再决定接收策略。

Go encoding/json 区分 JSON 字符串 123、JSON 数字 123 与 int 字段的类型映射边界
图1:Go int 字段接收 JSON 数据时,带引号的 123 与未带引号的 123 属于不同输入类型。

固定返回数字字符串时使用 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 输入字段定义结果适用判断
123int整数标准 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 是零值、可选字段还是错误;如果字段必须存在,建议用指针或额外的存在性标记表达,而不要让零值承担两种含义。

Go FlexibleInt 自定义 JSON 整数类型处理数字、数字字符串、null、strconv.ParseInt 与溢出错误
图2:自定义 FlexibleInt 把允许的 JSON 表示、整数范围和错误出口集中在一个字段边界。

上线前用边界样例确认整数没有被悄悄改写

这类字段最容易在“能解析”之后留下更隐蔽的问题。不要只测 "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",历史格式混用则把转换策略收口到自定义类型,并用边界测试守住整数范围。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Java List 删除整数时怎么区分下标和元素Java List 删除整数时怎么区分下标和元素
上一篇
Java List 删除整数时怎么区分下标和元素
Python dataclass 的列表默认值为什么要用 default_factory
下一篇
Python dataclass 的列表默认值为什么要用 default_factory
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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推荐
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    158次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    87次使用
  • ClickPrompt:AI提示词生成与优化工具,支持Stable Diffusion、ChatGPT及代码辅助
    ClickPrompt
    ClickPrompt是一款专为AI提示词编写者设计的开源在线工具,支持Stable Diffusion绘图、ChatGPT对话及GitHub Copilot代码辅助。提供Prompt自动生成、一键运行、社区分享及可视化优化功能,帮助用户高效获取精准AI输出。
    46次使用
  • PromptHero官网:AI提示词搜索、优化与学习平台,支持Midjourney/Stable Diffusion
    PromptHero
    PromptHero是专业的AI提示词搜索引擎与优化平台,支持Stable Diffusion、Midjourney等主流模型。提供海量提示词库、分类搜索、在线课程及社区互动,助力用户高效生成高质量AI图像与文本。
    30次使用
  • OpenArt免费开源指南:Stable Diffusion Prompt Book提示词手册详解
    Stable Diffusion Prompt Book
    深入解析OpenArt推出的Stable Diffusion Prompt Book,这本免费的开源提示词指南涵盖从基础语法到高级技巧,提供风格化词库与参数建议,助您优化AI绘画生成效果。
    30次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码