
Go 的 encoding/json 包仅序列化导出(首字母大写)字段;嵌入结构体若无导出字段,将生成空 JSON 对象 {}。
go 的 `encoding/json` 包仅序列化导出(首字母大写)字段;嵌入结构体若无导出字段,将生成空 json 对象 `{}`。
在 Go 中,结构体嵌入(embedding)是实现组合与代码复用的重要机制,但其与 json.Marshal 的交互常引发困惑:明明定义了嵌入字段,却得到空 JSON {}。根本原因在于 Go 的反射机制与 JSON 编码器的可见性规则——encoding/json 只能访问导出字段(即首字母大写的字段),对非导出字段(小写开头)完全不可见,无论其是否来自嵌入结构体。
以下是一个典型问题示例:
type Parent struct {
name string // 非导出字段 → JSON 中被忽略
value int // 非导出字段 → JSON 中被忽略
}
type Child struct {
Parent // 嵌入 Parent
}调用 json.Marshal(Child{}) 将返回 []byte("{}"),而非预期的 {"name":"","value":0}——因为 name 和 value 均未导出。
✅ 正确做法:将嵌入结构体中的字段改为导出形式,并注意命名冲突:
type Parent struct {
Name string `json:"name"` // 导出字段,可被 JSON 编码
Value int `json:"value"` // 导出字段
}
type Child struct {
Parent
// 若需额外字段,也须导出
ID uint64 `json:"id,omitempty"`
}此时:
c := Child{
Parent: Parent{Name: "Alice", Value: 42},
ID: 1001,
}
data, _ := json.Marshal(c)
fmt.Println(string(data)) // 输出:{"name":"Alice","value":42,"id":1001}⚠️ 注意事项:
- 嵌入结构体本身无需导出,但其内部字段必须导出才能参与 JSON 编码;
- 若嵌入结构体含有同名方法(如 Name()),而字段也命名为 Name,会导致编译错误(字段与方法重名)。此时应重命名字段(如 NameVal)或方法(如 GetName()),避免冲突;
- 可使用 json 标签精细控制字段名、忽略空值(omitempty)或强制忽略(-);
- 嵌入多层结构体时,导出规则逐层生效:只有最内层导出字段才可被序列化。
总结:Go 的 JSON 序列化严格遵循“导出可见性”原则。嵌入结构体不是魔法开关,它不改变字段的导出状态。要确保数据正确序列化,请始终检查所有参与编码的字段是否以大写字母开头——这是嵌入结构体 JSON 处理中最关键、也最容易被忽视的基础约束。










