gin返回结构体字段默认驼峰因go导出规则(首字母大写)与json包原样序列化所致;需用json tag显式指定下划线形式如json:"user_name",且字段必须可导出、tag必须显式声明。

为什么 Gin 返回的结构体字段默认是驼峰,而数据库或前端要下划线?
因为 Go 的 json 包默认按字段名原样序列化,而 Go 结构体字段必须首字母大写才能导出(如 UserName),导致 JSON 输出为 userName。但很多数据库字段、OpenAPI 规范或前端约定用 user_name,这就需要显式控制序列化后的 key 名。
直接在 struct tag 里加 json 标签是最简单可靠的方式
无需引入额外库或中间层,Go 原生支持。只要确保字段可导出(首字母大写),并在 json tag 中指定小写下划线形式即可:
type User struct {
ID int `json:"id"`
UserName string `json:"user_name"`
CreatedAt time.Time `json:"created_at"`
}
- 字段名本身(
UserName)不影响序列化结果,只用于 Go 内部访问 -
json:"user_name"完全决定输出键名,大小写和下划线都按你写的来 - 如果字段值为空或零值,且你想跳过该字段,可追加
omitempty:json:"user_name,omitempty" - 不加
jsontag 的字段(哪怕可导出)会被忽略;未导出字段(小写首字母)永远不参与 JSON 序列化
批量转换字段名?别依赖反射自动改,容易出错
有人想写个通用函数,自动把 UserName → user_name,但实际没必要,也不推荐:
- Go 没有运行时修改 struct tag 的能力,所谓“自动转换”只能靠反射 + 重新构造 map,性能差、丢失类型信息、无法处理嵌套或指针字段
- JSON 序列化阶段无法拦截或重写字段名,
json.Marshal只认 tag,不认变量名逻辑 - 真正需要统一规则的场景(如全项目字段对齐 DB),应该用代码生成工具(如
stringer或自定义go:generate脚本)生成带正确 tag 的 struct,而不是运行时补救 - 若已有大量驼峰字段结构体,逐个加 tag 是唯一稳当做法;IDEA 或 VS Code 都支持正则批量替换(例如匹配
`json:"(\w+)"`并替换成对应下划线形式)
注意 gin.H 和匿名 struct 不会自动转,必须手动写 key
gin.H 是 map[string]interface{} 的别名,它不走 struct tag 流程,所有 key 都是你手写的字符串:
c.JSON(200, gin.H{
"user_name": user.UserName,
"created_at": user.CreatedAt,
})
- 这里
"user_name"是字面量,和结构体字段名完全无关 - 如果你误写成
"UserName"或"username",输出就是那样,不会自动修正 - 混用
gin.H和 struct 时,字段命名一致性得靠人工维护,建议全项目统一用 struct + tag 方式,避免两种风格并存
encoding/json 包决定。最容易被忽略的一点是:**struct 字段必须可导出(首字母大写),且 json tag 必须显式声明,缺一不可**——漏掉任一条件,字段就消失或变成驼峰。











