在Go语言的日常开发中,JSON作为常用的数据交换格式,解析操作是非常高频的需求。但实际业务返回的JSON数据往往不会完全符合标准的结构化定义,比如有些整数字段会被编码成字符串形式,或者部分字段可能返回Null值,这类情况如果直接用标准库的json.Unmarshal解析,很容易出现类型不匹配、空指针错误等问题。本文就针对包含字符串编码整数和Null值的JSON解析场景,给出完整的Go语言实践方案。

常见的问题场景
我们先看一个典型的JSON数据示例,它同时包含字符串编码的整数和Null值:
{
"user_id": "12345",
"age": null,
"score": "98",
"name": "张三"
}
在这个JSON中,user_id和score字段本身是整型数据,但是被编码成了字符串;age字段的值是Null,对应的Go结构体字段如果是int类型的话,直接解析就会报错。接下来我们分步骤解决这个问题。
基础解析的问题演示
如果我们先按照常规思路定义结构体解析,会出现什么情况呢?先定义如下结构体:
package main
import (
"encoding/json"
"fmt"
)
type User struct {
UserID int `json:"user_id"`
Age int `json:"age"`
Score int `json:"score"`
Name string `json:"name"`
}
func main() {
jsonStr := `{
"user_id": "12345",
"age": null,
"score": "98",
"name": "张三"
}`
var user User
err := json.Unmarshal([]byte(jsonStr), &user)
if err != nil {
fmt.Println("解析错误:", err)
return
}
fmt.Printf("解析结果: %+vn", user)
}
运行这段代码会直接返回错误:json: cannot unmarshal string into Go struct field User.user_id of type int,因为字符串类型的"12345"无法直接转换为int类型的UserID字段,同时null也无法转换为int类型的Age字段。
自定义类型处理字符串编码的整数
要解决字符串编码整数的问题,我们可以自定义一个类型,实现json.Unmarshaler接口,在自定义的UnmarshalJSON方法中处理字符串到整数的转换逻辑。具体实现如下:
package main
import (
"encoding/json"
"fmt"
"strconv"
)
// 定义自定义类型,用于处理字符串编码的整数
type StringInt int
// 实现UnmarshalJSON方法
func (si *StringInt) UnmarshalJSON(data []byte) error {
// 先尝试直接解析为整数
var i int
if err := json.Unmarshal(data, &i); err == nil {
*si = StringInt(i)
return nil
}
// 如果失败,尝试解析为字符串,再转换为整数
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
// 字符串转整数
num, err := strconv.Atoi(s)
if err != nil {
return err
}
*si = StringInt(num)
return nil
}
这个自定义类型StringInt会优先尝试把JSON数据解析为整数,如果失败再解析为字符串,再调用strconv.Atoi转换为整数,这样就可以兼容整数和字符串编码的整数两种格式。
处理Null值的方案
对于JSON中的Null值,Go标准库提供了sql.NullInt64、sql.NullString等类型,也可以自定义可空类型。如果是整型字段的Null值,我们可以使用自定义的NullableInt类型来处理:
// 自定义可空整数类型
type NullableInt struct {
Value int
Valid bool // 标记值是否有效,即是否为null
}
// 实现UnmarshalJSON方法
func (ni *NullableInt) UnmarshalJSON(data []byte) error {
// 如果是null,设置Valid为false
if string(data) == "null" {
ni.Valid = false
return nil
}
// 先尝试解析为整数
var i int
if err := json.Unmarshal(data, &i); err == nil {
ni.Value = i
ni.Valid = true
return nil
}
// 尝试解析为字符串,再转整数
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
num, err := strconv.Atoi(s)
if err != nil {
return err
}
ni.Value = num
ni.Valid = true
return nil
}
这个类型可以同时处理Null值、整数字面量、字符串编码的整数三种情况,Valid字段可以用来判断原始JSON中该字段是否为Null。
完整解析示例
我们把上面的自定义类型整合到结构体中,完成完整的解析逻辑:
package main
import (
"encoding/json"
"fmt"
"strconv"
)
type StringInt int
func (si *StringInt) UnmarshalJSON(data []byte) error {
var i int
if err := json.Unmarshal(data, &i); err == nil {
*si = StringInt(i)
return nil
}
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
num, err := strconv.Atoi(s)
if err != nil {
return err
}
*si = StringInt(num)
return nil
}
type NullableInt struct {
Value int
Valid bool
}
func (ni *NullableInt) UnmarshalJSON(data []byte) error {
if string(data) == "null" {
ni.Valid = false
return nil
}
var i int
if err := json.Unmarshal(data, &i); err == nil {
ni.Value = i
ni.Valid = true
return nil
}
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
num, err := strconv.Atoi(s)
if err != nil {
return err
}
ni.Value = num
ni.Valid = true
return nil
}
type User struct {
UserID StringInt `json:"user_id"`
Age NullableInt `json:"age"`
Score StringInt `json:"score"`
Name string `json:"name"`
}
func main() {
jsonStr := `{
"user_id": "12345",
"age": null,
"score": "98",
"name": "张三"
}`
var user User
err := json.Unmarshal([]byte(jsonStr), &user)
if err != nil {
fmt.Println("解析错误:", err)
return
}
fmt.Printf("UserID: %dn", user.UserID)
fmt.Printf("Age Valid: %v, Age Value: %dn", user.Age.Valid, user.Age.Value)
fmt.Printf("Score: %dn", user.Score)
fmt.Printf("Name: %sn", user.Name)
}
运行这段代码,输出结果如下:
UserID: 12345 Age Valid: false, Age Value: 0 Score: 98 Name: 张三
可以看到,字符串编码的user_id和score都被正确转换为了整数,null值的age字段的Valid为false,符合我们的预期。
注意事项
- 自定义UnmarshalJSON方法时,要注意处理所有可能的JSON数据类型,避免遗漏场景导致解析错误
- 如果业务中Null值需要区分零值和未传值,一定要用Valid这类标记位,不要直接用零值判断,因为整数零值和Null在业务语义上往往是不同的
- 如果JSON中的字符串编码整数可能包含非数字字符,需要在转换逻辑中增加校验,避免转换失败
- 处理大量类似字段时,可以把自定义类型的逻辑封装成通用方法,减少重复代码
总结
解析包含字符串编码整数和Null值的JSON数据,核心是自定义类型实现UnmarshalJSON接口,在方法中按照业务需求处理不同格式的JSON数据。通过自定义StringInt和NullableInt类型,我们可以灵活兼容多种异常格式的JSON数据,避免解析报错。实际开发中可以根据具体的业务场景调整自定义类型的转换逻辑,满足不同的解析需求。
GoJSON解析字符串编码整数Null值处理json_Unmarshal修改时间:2026-06-12 02:24:44