
本文介绍如何将 SQL 查询(如 SELECT * FROM policy)的结果按 policy_name 分组,动态构建多层级 Go 结构体,并正确序列化为符合前端需求的 JSON 格式。
本文介绍如何将 sql 查询(如 select * from policy)的结果按 `policy_name` 分组,动态构建多层级 go 结构体,并正确序列化为符合前端需求的 json 格式。
在 Go Web 开发中,常需将数据库查询结果组织为嵌套 JSON 结构返回给前端(例如 Vue/React)。以 policy 表为例,目标是将多行记录按 name 字段聚合为顶层键,每个键对应一个 table 结构体,其 P 字段为该策略下所有参数的切片。
首先,需正确定义结构体——注意字段名首字母大写以导出,且 json 标签应与目标 JSON 键一致。同时,table 中 P 字段类型应为 []Parameters(切片),而非单个 Parameters:
type Table struct { // 推荐使用 PascalCase 首字母大写
Policy string `json:"policy"`
P []Parameters `json:"parameters"`
}
type Parameters struct {
PolicyID string `json:"policy_id"`
ClassID string `json:"class_id"`
Name string `json:"name"`
// 其他字段...
}关键在于:不能逐行直接构造 Table 实例,而应以 Name 为分组键,用 map[string]Table 累积数据。典型处理流程如下:
- 初始化空映射
policyMap := make(map[string]Table); - 遍历
rows,对每行扫描到Parameters实例p; - 若
p.Name在映射中不存在,则初始化对应Table{Policy: p.Name, P: []Parameters{}}; - 将
p追加至policyMap[p.Name].P切片; - 最终将
map转为切片(或直接json.Marshal映射)。
完整示例代码:
func getPolicyData(db *sql.DB) (map[string]Table, error) {
rows, err := db.Query("SELECT policy_id, class_id, name FROM policy")
if err != nil {
return nil, err
}
defer rows.Close()
policyMap := make(map[string]Table)
for rows.Next() {
var p Parameters
if err := rows.Scan(&p.PolicyID, &p.ClassID, &p.Name); err != nil {
return nil, err
}
// 按 name 分组:首次出现时初始化,后续直接追加
if _, exists := policyMap[p.Name]; !exists {
policyMap[p.Name] = Table{
Policy: p.Name,
P: make([]Parameters, 0),
}
}
policyMap[p.Name].P = append(policyMap[p.Name].P, p)
}
if err = rows.Err(); err != nil {
return nil, err
}
return policyMap, nil
}
// 使用示例(如 Gin 路由 handler)
func handlePolicies(c *gin.Context) {
policies, err := getPolicyData(db)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, policies) // 自动序列化为 JSON
}⚠️ 注意事项:
-
rows.Scan的参数顺序必须严格匹配 SQL 查询字段顺序; -
Parameters.Name是分组依据,确保数据库中该字段非空且具有区分度; - 若需保持插入顺序(如前端依赖策略顺序),建议最终转为有序切片:
for _, t := range policyMap { result = append(result, t) }; - 生产环境应使用
sqlx或gorm等库简化扫描逻辑,避免手动维护字段映射。
通过此模式,SQL 返回的扁平结果集即可高效转化为嵌套 JSON:
{
"policyName1": {
"policy": "policyName1",
"parameters": [
{"policy_id":"1","class_id":"01","name":"policyName1"},
{"policy_id":"2","class_id":"02","name":"policyName1"}
]
},
"policyName2": { ... }
}


















