
本文讲解如何通过定义嵌套结构体(如 RatingPK 包裹 Rating),配合正确类型声明与初始化,使 Go 程序序列化时自动输出符合预期的嵌套 JSON 格式。
本文讲解如何通过定义嵌套结构体(如 `ratingpk` 包裹 `rating`),配合正确类型声明与初始化,使 go 程序序列化时自动输出符合预期的嵌套 json 格式。
在 Go 中,若希望将一个对象(如 Rating)作为字段“嵌入”到另一个对象(如 RatingPK)中,并在 JSON 序列化时呈现为 { "ratingsPK": { ... } } 的结构,关键在于:结构体定义、返回类型一致性、以及避免手动拼接 JSON 字符串。
✅ 正确做法:使用嵌套结构体 + 正确返回类型
原始代码中 searchUserRatings 函数声明返回 []Rating,但实际想返回的是 []RatingPK —— 这是核心问题。只需修正返回类型和变量声明,Go 的 json.Marshal() 就会自动按结构体字段名生成嵌套 JSON。
以下是优化后的完整示例:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
type Rating struct {
MovieId int `json:"movieId"`
UserId int `json:"userId"`
RatingId int `json:"rating_id"`
Rating int `json:"rating"`
Timestamp time.Time `json:"timestamp"`
}
type RatingPK struct {
RatingsPK Rating `json:"ratingsPK"` // 字段名小写 + json tag 控制输出键名
}
func searchUserRatings(id string) ([]RatingPK, error) {
rows, err := db.Query("SELECT * FROM ratings WHERE userId = ?", id)
if err != nil {
return nil, err
}
defer rows.Close() // ✅ 建议移至函数开头后立即 defer,避免遗漏
var ratings []RatingPK
for rows.Next() {
var rate Rating
// 注意:确保 Scan 字段顺序与 SELECT 列顺序严格一致
if err := rows.Scan(&rate.MovieId, &rate.UserId, &rate.RatingId, &rate.Rating, &rate.Timestamp); err != nil {
return nil, err
}
// 直接构造 RatingPK(无需 Init 方法,更简洁)
ratings = append(ratings, RatingPK{RatingsPK: rate})
}
if err := rows.Err(); err != nil {
return nil, err
}
return ratings, nil
}
? 调用示例(含 JSON 输出)
func handler(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
ratings, err := searchUserRatings(id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(ratings) // 自动输出:[{"ratingsPK":{...}}, ...]
}
⚠️ 注意事项
-
JSON Tag 命名:
RatingsPK Rating \json:"ratingsPK"`中的 tag 决定了最终 JSON 键名;字段名RatingsPK首字母大写保证可导出(否则json` 包无法访问)。 -
避免冗余方法:
Init()方法非必需,直接字段赋值更符合 Go 惯例,也利于零值安全与结构体字面量初始化。 -
错误处理增强:
rows.Err()应在循环后检查,确保没有扫描期间未捕获的错误。 -
时间类型兼容性:若数据库
timestamp可能为NULL或非法值,建议使用sql.NullTime并自定义 JSON 序列化逻辑。
✅ 总结
实现 "ratingsPK": { ... } 的嵌套 JSON,本质是语义建模而非字符串操作:
- 定义外层结构体(
RatingPK),内嵌目标结构体(Rating); - 使用
jsontag 精确控制字段键名; - 返回该外层结构体切片(
[]RatingPK); - 交由标准库
json.Marshal/json.Encoder处理序列化。
这样既保持类型安全,又完全符合 Go 的声明式、结构化设计哲学。










