使用sql.NullString时c.IndentedJSON无法输出正确JSON数组的问题
解决sql.NullString序列化JSON为空对象的问题
问题原因
sql.NullString的内部字段String和Valid都是小写开头的非导出字段,Go标准库的encoding/json包只会序列化结构体中大写开头的导出字段。直接序列化包含sql.NullString的结构体时,这些字段会被序列化为空对象{},最终返回的数组看起来全是空对象。
而你用fmt打印结构体时能看到内容,是因为fmt包会输出结构体的所有字段(包括非导出的),和JSON序列化的规则完全不同。
解决方案
1. 使用第三方库的可序列化null类型
推荐使用gopkg.in/guregu/null.v4库,它提供的null.String类型原生支持JSON序列化:
- 值有效时,序列化为对应字符串
- 值无效(数据库NULL)时,序列化为
null
只需把代码里的sql.NullString替换为null.String,直接用c.IndentedJSON输出就能得到正确结果。
2. 自定义可序列化的NullString类型
如果不想引入第三方库,可以自己封装sql.NullString并实现json.Marshaler接口:
import ( "database/sql" "encoding/json" ) // 自定义NullString,继承sql.NullString的结构 type NullString sql.NullString // 实现JSON序列化方法 func (ns NullString) MarshalJSON() ([]byte, error) { if !ns.Valid { // 无效时返回JSON null return []byte("null"), nil } // 有效时返回字符串的JSON表示 return json.Marshal(ns.String) } // 可选:实现反序列化方法 func (ns *NullString) UnmarshalJSON(data []byte) error { if string(data) == "null" { ns.String = "" ns.Valid = false return nil } var s string if err := json.Unmarshal(data, &s); err != nil { return err } ns.String = s ns.Valid = true return nil }
之后结构体里使用自定义的NullString代替sql.NullString,序列化时就能正确输出null或对应字符串。
3. 转换为普通指针类型后序列化
在返回响应前,把数据库模型转换成API专用结构体,用*string表示可能为null的字符串:
// 数据库查询用的模型 type Category struct { ID int `db:"id"` Name sql.NullString `db:"name"` } // API响应专用模型 type APICategory struct { ID int `json:"id"` Name *string `json:"name"` } func yourHandler(c *gin.Context) { var categories []Category // 从数据库查询数据... // 转换为API响应格式 apiCategories := make([]APICategory, len(categories)) for i, cat := range categories { var name *string if cat.Name.Valid { name = &cat.Name.String } apiCategories[i] = APICategory{ ID: cat.ID, Name: name, } } c.IndentedJSON(http.StatusOK, apiCategories) }
这种方式完全基于标准库,适合不想引入依赖或需要精确控制响应格式的场景。
内容的提问来源于stack exchange,提问作者Josip Žaja
相关产品推荐
相关产品推荐

