nil切片序列化为null,空切片(如[]string{}或make([]string,0))序列化为[];encoding/json依据切片底层array指针是否为nil判断,而非len或cap。

Go 里 nil 切片和 []string{} 在 JSON 中导出结果不同
直接结论:nil 切片序列化为 null,空切片(如 []string{} 或 make([]string, 0))序列化为 []。这不是 bug,是 encoding/json 包的明确行为。
常见错误现象:API 返回字段本应是空数组 "items": [],结果前端收到 "items": null,导致 JS 解构报错或渲染异常。
-
var s []string→s == nil为 true,json.Marshal(s)输出null -
s := []string{}或s := make([]string, 0)→s == nil为 false,json.Marshal(s)输出[] - 即使
len(s) == 0和cap(s) == 0都成立,只要底层array指针是nil,就走null分支
json.Marshal 判断切片是否为 nil 的依据是底层指针
Go 的切片结构体(slicestruct)包含 array unsafe.Pointer、len、cap 三个字段。encoding/json 在编码时只检查 array == nil,不看 len 或 cap。
这意味着:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
-
var s []int:array是nil,JSON 输出null -
s := make([]int, 0):array指向运行时全局zerobase地址(非nil),JSON 输出[] -
s := []int{}:同上,array非nil,输出[] - 哪怕你后续
s = nil,它就立刻变回null—— 这个赋值是有效的
定义切片时该用 make 还是字面量?场景决定
两者在 JSON 行为上等价,但语义和可读性略有差异:
- 想明确表达“这是一个已初始化、可安全
append的空容器”,用[]string{}更直观 - 需要指定容量(比如预分配空间避免多次扩容),必须用
make([]string, 0, 16) - 在 struct 字段初始化中,推荐统一用字面量:
type Resp struct { Data []int `json:"data"` }; resp := Resp{Data: []int{}} - 不要依赖
var s []string; s = append(s, "x")—— 虽然能工作,但第一次append会触发内存分配,且初始状态仍是nil,JSON 不安全
前端对接时最容易被忽略的点
后端返回 null 还是 [],对前端来说不是“差不多”,而是类型断裂:
- JS 中
arr?.map(...)在arr === null时直接报错,而arr = []可以安全调用 - TypeScript 接口若声明
items: string[],但实际收到null,就会绕过类型检查(因为null可赋给任意类型) - Swagger/OpenAPI 文档若未标注字段可为空,前端生成代码可能默认按非空数组处理
- 修复成本低:只要确保 struct 字段初始化时不留
var声明的裸切片,全部显式赋值为[]T{}或make([]T, 0)

















