go结构体字段首字母必须大写才能被json序列化,因json.marshal()仅导出首字母大写的字段;小写字段即使有json tag也会被忽略;嵌套结构体、类型名、时间/指针/切片字段同理需大写。

Go结构体字段首字母必须大写才能被JSON序列化
Gin 的 c.JSON() 底层用的是 Go 标准库 json.Marshal(),它只会导出(exported)字段——也就是首字母大写的字段。小写字段如 name string 在序列化后永远是空的,哪怕你传了值、也加了 json:"name" tag,照样输出 {} 或缺失该字段。
常见错误写法:
type User struct {
name string `json:"name"` // ❌ 小写,不导出,JSON里看不到
age int `json:"age"`
}
正确写法:
type User struct {
Name string `json:"name"` // ✅ 首字母大写
Age int `json:"age"`
}
- struct tag 中的键名(如
"name")决定 JSON 输出字段名,和 Go 字段名大小写无关 - 但 Go 字段本身必须首字母大写,否则
json.Marshal()直接跳过 - 别依赖 IDE 自动补全或复制粘贴旧代码——检查每个字段是否导出
gin.H 和 map[string]interface{} 的字段名由 key 决定,无大小写限制
如果你用 gin.H 或 map[string]interface{} 返回数据,字段名完全由 map 的 key 控制,跟 Go 变量命名规则无关。这时候大小写问题只影响你写 key 时的手感,不影响序列化结果。
例如:
c.JSON(200, gin.H{
"userName": "alice", // ✅ key 是字符串,任意大小写都行
"user_age": 25,
})
这种写法灵活,适合快速原型或动态字段;但缺点也很明显:
- 没有类型安全,编译期无法检查字段拼写错误
- 无法配合
go-playground/validator做参数校验 - IDE 不支持跳转、重构、自动补全
所以建议:简单响应用 gin.H,业务模型用结构体 + 大写字段。
想让 JSON 字段全小写?改 struct tag,别动 Go 字段名
有人想“统一小写输出”,于是把结构体字段全改成小写,结果发现字段丢了。这是方向反了。正确做法是保持字段首字母大写,只改 tag:
type Product struct {
ID uint64 `json:"id"` // 输出为 "id"
Title string `json:"title"` // 输出为 "title"
Price float64 `json:"price"`
}
这样既满足导出要求,又控制了 JSON 键名风格。注意:
- tag 值区分大小写:
"ID"和"id"是两个不同 key - 如果 tag 省略(如
ID uint64),默认使用字段名原样("ID"),不是小写 - 不要写
ID uint64 `json:"ID"`这种冗余写法,除非前端强依赖大写
嵌套结构体、时间字段、指针字段的大小写陷阱
结构体嵌套时,每一层都要检查字段是否导出。比如下面这个常见错误:
type Order struct {
ID uint64 `json:"id"`
User user `json:"user"` // ❌ user 是小写类型,其字段全不可导出
}
type user struct { // ❌ 类型名小写 → 所有字段都不导出
Name string `json:"name"`
}
正确写法是类型名和字段名都大写:
type Order struct {
ID uint64 `json:"id"`
User User `json:"user"`
}
type User struct { // ✅ 类型名大写
Name string `json:"name"`
}
其他易踩点:
-
time.Time字段没问题,但若用string接收时间字符串,字段仍需大写(CreatedAt string) - 指针字段如
*string同样要大写字段名,且注意 nil 安全(c.JSON()会输出null) - 切片字段如
Tags []string,字段名大写即可,无需额外处理
最麻烦的其实是团队协作时,有人习惯写小写字段+tag,本地测试能过,一部署就丢字段——因为开发环境没开严格 JSON 校验,线上日志又不打响应体。上线前用 curl 实际请求看一眼原始响应,比读十遍文档都管用。











