本文介绍如何利用 go 结构体的 json 标签(struct tags)将数据库查询结果准确序列化为嵌套 json,特别适用于一对多关系(如零售商与商品)的数据建模与 api 响应生成。
本文介绍如何利用 go 结构体的 json 标签(struct tags)将数据库查询结果准确序列化为嵌套 json,特别适用于一对多关系(如零售商与商品)的数据建模与 api 响应生成。
要实现从关系型数据库中查询出“一个零售商及其多个商品”并输出为符合预期的嵌套 JSON(如 {"id": "...", "name": "...", "products": [...]}),仅定义结构体是不够的——Go 的 json.Marshal() 默认使用字段名(首字母大写)作为 JSON 键,且无法自动处理一对多嵌套关系。关键在于两步协同:结构体字段的 JSON 标签声明 + 数据组装逻辑的合理实现。
首先,为结构体添加正确的 JSON 标签,确保序列化时键名与目标 JSON 一致:
type Retailer struct { // 建议使用单数命名规范(Retailer 而非 Retailers)
ID int `json:"id"`
Name string `json:"name"`
Products []Product `json:"products"`
}
type Product struct {
ID int `json:"id"`
Description string `json:"description"`
URL string `json:"url"`
}
⚠️ 注意:
- json:"id" 等标签控制序列化后的字段名(小写),而非结构体字段名(ID 首字母大写以导出);
- 字段名建议采用 Go 习惯的 ID、URL(而非 Id/Url),既保持可读性又符合 golint 规范;
- Products 字段类型为 []Product,支持嵌套数组序列化。
但仅靠结构体标签无法自动完成 JOIN 查询结果的嵌套填充。你的原始 SQL:
SELECT r.id, r.name, p.id, p.description, p.url FROM retailers r JOIN products p ON r.id = p.retailer_id -- 注意:原问题中 JOIN 条件有误,应为 p.retailer_id
会返回多行(每个商品一行),需在 Go 中手动聚合。推荐使用 sqlx 或原生 database/sql 配合分组逻辑:
rows, err := db.Query(query)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
retailerMap := make(map[int]*Retailer)
for rows.Next() {
var rID int
var rName, pDesc, pURL string
var pID int
if err := rows.Scan(&rID, &rName, &pID, &pDesc, &pURL); err != nil {
log.Fatal(err)
}
// 初始化或获取已存在的 Retailer
if _, exists := retailerMap[rID]; !exists {
retailerMap[rID] = &Retailer{ID: rID, Name: rName}
}
// 追加 Product 到对应 Retailer
retailerMap[rID].Products = append(retailerMap[rID].Products,
Product{ID: pID, Description: pDesc, URL: pURL})
}
// 转为切片(若只需单个零售商,取第一个值即可)
var retailers []Retailer
for _, r := range retailerMap {
retailers = append(retailers, *r)
}
// 序列化为 JSON
data, _ := json.MarshalIndent(retailers, "", " ")
fmt.Println(string(data))
✅ 总结:
- 结构体标签是 JSON 输出格式的基础保障,没有它,Id → "Id",Url → "Url",无法匹配需求中的小写键;
- JOIN 查询需配合内存聚合,Go 不像 ORM(如 GORM)自动处理嵌套预加载,需手动按主键分组;
- 生产环境建议使用 sqlx.StructScan 或 GORM.Preload("Products") 简化开发,但底层原理仍依赖标签与关联逻辑。
正确配置标签 + 合理组织数据组装流程,即可稳定输出结构清晰、符合 API 规范的嵌套 JSON 响应。











