
Go 的 json.Marshal 要求结构体字段必须是导出(首字母大写)的公共字段;若使用小写私有字段(如 age、sibling),JSON 编码器将忽略它们,导致空对象 {}。修复只需将字段名改为大写首字母即可支持递归嵌套的序列化与反序列化。
go 的 `json.marshal` 要求结构体字段必须是导出(首字母大写)的公共字段;若使用小写私有字段(如 `age`、`sibling`),json 编码器将忽略它们,导致空对象 `{}`。修复只需将字段名改为大写首字母即可支持递归嵌套的序列化与反序列化。
在 Go 中,encoding/json 包本身完全支持递归类型(如链表、树、图等结构),关键限制不在于递归本身,而在于字段的可见性。Go 语言规定:只有首字母大写的字段才是导出字段(public),才能被外部包(包括 json 包)访问和序列化;小写字段属于包内私有,json 包无法读取或写入,因此会静默跳过——这正是 json.Marshal(&Dog{age: 5, sibling: nil}) 返回 "{}" 的根本原因。
✅ 正确做法:将字段名改为导出形式,并可选添加 JSON 标签以控制键名:
type Dog struct {
Age int `json:"age"`
Sibling *Dog `json:"sibling,omitempty"`
}
完整可运行示例:
package main
import (
"encoding/json"
"fmt"
)
type Dog struct {
Age int `json:"age"`
Sibling *Dog `json:"sibling,omitempty"`
}
func main() {
// 构建递归结构:dog1 → dog2 → nil
dog2 := &Dog{Age: 3}
dog1 := &Dog{Age: 5, Sibling: dog2}
data, err := json.Marshal(dog1)
if err != nil {
panic(err)
}
fmt.Println(string(data)) // 输出:{"age":5,"sibling":{"age":3}}
// 反序列化也完全支持递归
var restored Dog
if err := json.Unmarshal(data, &restored); err != nil {
panic(err)
}
fmt.Printf("Restored: %+v\n", restored) // {Age:5 Sibling:&{Age:3 Sibling:<nil>}}
}</nil>
⚠️ 注意事项:
-
无限递归风险:若结构形成环(如
a.Sibling = b; b.Sibling = a),json.Marshal会因栈溢出 panic(Go 1.20+ 默认限制深度为 1000 层)。生产环境应提前检测环状引用或改用自定义MarshalJSON方法做截断。 -
零值处理:使用
omitempty标签可避免序列化nil指针字段(如Sibling: nil不出现在输出中)。 - 性能提示:深度嵌套结构可能影响序列化性能,必要时可考虑扁平化设计或分步序列化。
总结:Go 原生支持递归类型的 JSON 编解码,无需第三方库;只需确保字段导出、避免环引用,并合理使用结构标签,即可安全、高效地处理树形、链表等常见递归数据结构。











