登录
首页 >  Golang >  Go教程

nil 切片与 empty 切片 JSON 序列化区别

时间:2026-05-13 23:04:26 446浏览 收藏

在 Go 的 JSON 序列化中,nil 切片(底层指针为 nil)会被编码为 `null`,而空切片(指针非 nil 但长度为 0)则编码为 `[]`,这一本质差异源于 `json.Marshal` 直接检查切片底层指针而非 `len()`,导致前后端对字段“未传”与“传了空数组”的语义理解截然不同——前者易引发前端解析异常或逻辑误判,后者则明确表达存在性;反序列化时 `null` 恢复为 nil 切片、`[]` 恢复为非 nil 空切片,因此必须用 `s == nil` 而非 `len(s) == 0` 准确判断客户端是否省略该字段;此外,在接口或泛型场景下需借助反射安全识别 nil 切片,而初始化策略(`var s []T` vs `s := []T{}`)应严格依据 API 语义需求选择,因为这一行为是 Go 运行时对切片三元组的硬编码契约,无法绕过或配置。

json.Marshal 会把 nil 切片转成 null,empty 切片转成 []

这是最直接、最常踩坑的差异。Go 的 json.Marshal 不看 len(),它看切片底层的 array 指针是否为 nil:指针为 nil → 输出 null;指针非 nil(哪怕指向零长数组)→ 输出 []

示例:

type Resp struct {
    Tags []string `json:"tags"`
}
json.Marshal(Resp{Tags: nil})        // {"tags":null}
json.Marshal(Resp{Tags: []string{}}) // {"tags":[]}
  • 前端收到 {"tags":null} 通常会当作字段缺失或未初始化,可能触发默认逻辑或报错
  • 前端收到 {"tags":[]} 明确知道“有这个字段,但值为空数组”,更利于一致性处理
  • API 版本升级时若旧客户端不处理 null,而新逻辑返回了 nil 切片,就容易崩在前端 JSON 解析阶段

json.Unmarshal 反向行为:null → nil,[] → empty

反序列化时,json.Unmarshal 严格按 JSON 值还原语义:null 被赋给结构体字段后是 nil 切片;[] 被赋值后是长度为 0 的非 nil 切片。

这意味着你不能只靠 len(s) == 0 判断“客户端有没有传这个字段”:

  • len(tags) == 0nil[] 都成立 —— 无法区分“没传”和“传了空数组”
  • 正确做法是先判断 tags == nil,再结合业务决定是否允许该字段缺失
  • 如果结构体字段是 *[]string,那 null 会解成 nil 指针,[] 会解成非 nil 指针指向 empty 切片,语义更清晰但用得少

接口类型或泛型参数中,nil 切片会“消失”

当你把 nil 切片赋给 interface{} 或传入泛型函数时,v == nil 会恒为 false —— 因为接口值本身非 nil(它包装了一个 nil 切片)。

这时候想安全判断,必须用反射:

func IsNilSlice(v interface{}) bool {
    rv := reflect.ValueOf(v)
    return rv.Kind() == reflect.Slice && rv.IsNil()
}
  • 直接写 v == nil 在泛型函数里编译失败(类型不确定)
  • reflect.ValueOf(v).IsNil() 前必须确认 Kind()reflect.Slice,否则 panic
  • 这种反射判断开销小,但别在 hot path 上高频调用

初始化选择:var s []T 还是 s := []T{}?

取决于你要不要保留“未设置”这个语义层级:

  • var s []T:适合 API 输入字段,需区分 null(客户端省略)和 [](客户端显式传空)
  • s := []T{}make([]T, 0):适合内部临时容器,后续必走 append,且不希望下游看到 null
  • 别用 make([]T, 0, N) 来“优化” nil 切片——appendnil 切片同样高效,它内部会直接分配,手动预分配反而可能浪费内存

真正容易被忽略的是:JSON 序列化行为不是配置项,也不是可开关的特性,它是语言运行时对切片三元组(ptr/len/cap)的硬编码解释。一旦定义了结构体字段类型为 []T,你就已经绑定了这个语义契约。

好了,本文到此结束,带大家了解了《nil 切片与 empty 切片 JSON 序列化区别》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多Golang知识!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>