当前位置:首页 > 文章列表 > 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推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    578次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    619次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    650次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    613次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    429次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码