Go JSON 转换技巧:解决字段类型不一致
潘忠显 / 2025-12-10
对接外部 API 时,开发者经常会遇到一个令人头疼的问题:外部系统返回的 JSON 数据,其字段类型与我们 Go 结构体中预期的类型不一致。
典型地,API 可能将原本应该是数值的,错误地或者出于历史原因地包装在 JSON 字符串中返回(例如,"age": "30" 而不是标准的 "age": 30)。
如果直接尝试将这些 JSON 字段解码(Unmarshal)到 Go 结构体的 int 或 float64 字段中,Go 语言的 encoding/json 包会因为类型不匹配而报错。你使用 string 类型来定义,又每次都需要转换。
其实可以利用 Go 语言结构体标签(Tag)提供的强大功能,在不修改 Go 结构体内部类型(保持 int 等强类型)的前提下,兼容外部这种不规范的 JSON 格式。
一、使用 ,string 强制转换类型
在结构体字段的 json 标签后面添加 ,string 标签是解决上述问题的核心方法。
它指示 encoding/json 包在处理这个字段时,将 Go 结构体中的字段值视为一个 JSON 字符串来处理。
例如,以下定义允许 Go 结构体中的 Age 保持 int 类型,但告知 JSON 编码器和解码器,在 JSON 世界中,它对应的是一个字符串。
type Data struct {
Name string `json:"name"`
// Age 是 int 类型,但使用字符串进行编解码
Age int `json:"age,string"`
}
编解码效果
编码(Marshal: Go Struct -> JSON):当 Go 结构体字段 Age 的值为 30(整数)时,编码器会将其转换为 JSON 字符串 “30”。最终 JSON 输出将是:{"name":"Alice","age":"30"}。
解码(Unmarshal: JSON -> Go Struct):解码器会读取 JSON 输入中的 “age”:“45” 字符串。它会尝试将字符串中的内容 "45" 解析成一个整数,并成功赋值给 Go 结构体的 Age 字段(int(45))。
适用范围
这个 ,string 标签的强大之处在于,它适用于所有 Go 的基础类型,只要该类型有对应的标准库函数(如 strconv 包中的 ParseFloat、ParseBool 等)可以将其从字符串解析出来。比如将"3.14" 用于 float64 等类型,接受 "true" 或 "false" 用于 bool 类型。
但是,请注意,,string 不能直接用于复杂的复合类型,比如结构体(Struct)或切片(Slice),因为它只能处理扁平化的数据。
二、JSON的其他常用标签
除了 ,string 之外,encoding/json 包还提供了其他几种强大且常用的标签技巧,可以极大地增强结构体的灵活性。
如果定义一个结构体,不使用 json: 标签,在Marshal的时候,会只以同名的方式,产生 JSON 的 key,并且这里只有大些开头的字段——被导出的。
- JSON中命名常使用蛇形命名键(如
product_id),这个最普通的用法就出现了:通过json:"new_key_name"格式重命名,您可以将 Go 结构体中大驼峰命名的字段,映射到 JSON 中常用的蛇形命名键。 - 如果你有字段不想让其参与编解码,你可以通过
json:"-"标签来忽略字段,您可以完全阻止某个字段参与 JSON 的编码和解码。这在结构体包含敏感信息或只用于内部逻辑的字段时非常有用。 - 你还可以通过在标签中添加
,omitempty选项来忽略零值,这样int类型为0,string类型为""(空字符串),指针类型为nil在编码后的 JSON 中将看不到类似字段,从而减小 JSON 数据体积。
