
本文讲解如何在 go 中使用 mgo 驱动查询 mongodb 数据后,不直接序列化结构体切片,而是自定义输出为无数组包裹、以 endpoint 为键、time 为值的扁平化 json 对象。
本文讲解如何在 go 中使用 mgo 驱动查询 mongodb 数据后,不直接序列化结构体切片,而是自定义输出为无数组包裹、以 endpoint 为键、time 为值的扁平化 json 对象。
默认情况下,json.MarshalIndent 会将 []Slow 序列化为 JSON 数组(即带方括号的列表),但您期望的是一个纯键值映射对象(如 {"key1":"val1","key2":"val2"})。这本质上不是数据存储问题,而是响应格式化逻辑的调整——MongoDB 插入与结构体定义本身无需修改,关键在于查询后的数据组装方式。
首先,请确保查询结果按 Endpoint 字段有序,避免键序不确定导致输出不可预测(MongoDB 默认不保证返回顺序):
var results []Slow
err := collection.Find(nil).Sort("Endpoint").All(&results) // 注意字段名大小写匹配结构体标签(见下文)
if err != nil {
panic(err)
}
⚠️ 注意:Sort("endpoint") 中的 "endpoint" 应与 MongoDB 文档中实际字段名一致。若结构体字段 Endpoint 未显式指定 BSON 标签,默认映射为小写 endpoint;但为明确起见,建议为结构体添加 BSON 标签:
type Slow struct {
Endpoint string `bson:"endpoint"`
Time string `bson:"time"`
}
接下来,跳过 json.Marshal,手动构造目标 JSON 对象。以下代码生成严格符合您预期的缩进格式(无数组、键值对、换行缩进):
详细的 Three.js 3D 图形参考,涵盖场景设置、相机、几何体、材质、光照、动画、控制器、加载器、数学工具和调试。
w.Write([]byte{'{'})
for i, slow := range results {
if i > 0 {
w.Write([]byte{','})
}
// 使用 \n\t 实现换行+缩进,与 json.MarshalIndent(" ", " ") 效果对齐
w.Write([]byte(fmt.Sprintf("\n\t\"%s\":\"%s\"", slow.Endpoint, slow.Time)))
}
w.Write([]byte("\n}"))
✅ 输出示例:
{
"/api/endpoint1":"0.8s",
"/api/endpoint2":"0.7s"
}
? 进阶建议:若需更高可维护性或复用性,可封装为辅助函数:
func writeEndpointMap(w io.Writer, data []Slow) error {
w.Write([]byte{'{'})
for i, item := range data {
if i > 0 {
w.Write([]byte{','})
}
if _, err := fmt.Fprintf(w, "\n\t\"%s\":\"%s\"", item.Endpoint, item.Time); err != nil {
return err
}
}
w.Write([]byte("\n}"))
return nil
}
// 调用:writeEndpointMap(w, results)
总结:实现该格式的核心在于——放弃通用 JSON 序列化,改用流式字符串拼接。它轻量、可控,且完全规避了结构体到 map 的中间转换开销。只要确保查询排序与字段映射准确,即可稳定输出所需键值对 JSON 对象。










