
go 中结构体字段首字母小写会导致 `json` 包无法访问,从而生成空 json 对象;只需将字段名首字母大写并保持 json tag 正确,即可实现正常序列化。
在 Go 语言中,encoding/json 包仅能访问导出(exported)字段——即首字母为大写的字段。这是由 Go 的可见性规则决定的:小写字母开头的字段属于包私有(unexported),外部包(包括标准库中的 json)无法反射读取其值,因此 json.Marshal() 会忽略它们,最终返回空对象 {}。
观察原始代码:
type PodsCondensed struct {
pods []PodCondensed `json:"pods"` // ❌ 小写 pods → 非导出字段
}
type PodCondensed struct {
name string `json:"name"` // ❌ 小写 name → 非导出字段
colors []string `json:"colors"` // ❌ 小写 colors → 非导出字段
}尽管 json tag 存在且语法正确,但因所有字段均为 unexported,json.Marshal 实际上“看不见”任何可序列化的数据,故输出为空 JSON {}。
✅ 正确做法是将字段名首字母大写,同时保留 json tag 控制输出键名:
type PodsCondensed struct {
Pods []PodCondensed `json:"pods"` // ✅ 导出字段,映射为 "pods"
}
func (p *PodsCondensed) AddPod(pod PodCondensed) {
p.Pods = append(p.Pods, pod)
}
type PodCondensed struct {
Name string `json:"name"` // ✅ 导出字段,映射为 "name"
Colors []string `json:"colors"` // ✅ 导出字段,映射为 "colors"
}完整可运行示例:
package main
import (
"encoding/json"
"fmt"
)
type PodsCondensed struct {
Pods []PodCondensed `json:"pods"`
}
func (p *PodsCondensed) AddPod(pod PodCondensed) {
p.Pods = append(p.Pods, pod)
}
type PodCondensed struct {
Name string `json:"name"`
Colors []string `json:"colors"`
}
func main() {
fakePods := PodsCondensed{}
fakePod := PodCondensed{
Name: "tier2",
Colors: []string{"blue", "green"},
}
fakePods.AddPod(fakePod)
fmt.Printf("Go value: %+v\n", fakePods.Pods) // [{Name:"tier2" Colors:["blue" "green"]}]
jPods, err := json.Marshal(fakePods)
if err != nil {
panic(err)
}
fmt.Printf("JSON output: %s\n", string(jPods)) // {"pods":[{"name":"tier2","colors":["blue","green"]}]
}? 关键注意事项:
- 字段是否导出(首字母大小写)优先级高于 json tag —— 即使 tag 正确,未导出字段仍会被忽略;
- 方法、函数、接口等不受此限制,但 json 包只处理结构体字段;
- 若需保留字段私有性但又支持 JSON 序列化,可添加 getter 方法 + 自定义 MarshalJSON(),但对简单场景不推荐过度设计;
- 使用 json:",omitempty" 可跳过零值字段,但前提是该字段本身已导出。
总结:Go 的 JSON 序列化依赖于字段导出机制,牢记 “小写不可见,大写才可用”,是避免此类问题最直接有效的原则。










