
在 go 中使用 mgo(或官方驱动)从 mongodb 反序列化数据时,若结构体字段始终为空,最常见原因是字段未导出(即小写首字母),导致 bson 解析器无法访问和赋值。
在 go 中使用 mgo(或官方驱动)从 mongodb 反序列化数据时,若结构体字段始终为空,最常见原因是字段未导出(即小写首字母),导致 bson 解析器无法访问和赋值。
Go 语言的反射机制(包括 mgo 和官方 mongo-go-driver 所依赖的 bson 库)只能访问导出字段(即首字母大写的字段)。当结构体字段为 id、name(小写)时,它们是私有字段,BSON 解析器无法写入数据,因此即使数据库中存在 _id 和 nm 字段,反序列化后仍为空字符串。
✅ 正确做法:将字段名改为首字母大写,并通过 bson 标签精确映射 MongoDB 字段:
type StationConfig struct {
ID string `bson:"_id,omitempty"` // 映射 "_id",omitempty 表示插入时可省略
Name string `bson:"nm"` // 映射 "nm" 字段
}
同时注意:_id 在 MongoDB 中是 ObjectID 类型,若需完整支持(如后续查询、比较、转换为字符串),推荐使用 primitive.ObjectID(官方驱动)或 bson.ObjectId(旧版 mgo)。例如,使用现代官方驱动时更严谨的定义如下:
import "go.mongodb.org/mongo-driver/bson/primitive"
type StationConfig struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Name string `bson:"nm"`
}
而若继续使用已归档的 mgo.v2(如原问题所示),则应使用 bson.ObjectId:
import "gopkg.in/mgo.v2/bson"
type StationConfig struct {
ID bson.ObjectId `bson:"_id,omitempty"`
Name string `bson:"nm"`
}
⚠️ 注意事项:
- 字段必须导出:id → ID,name → Name,否则反射不可见;
- 标签名须与数据库字段严格匹配:bson:"nm" 对应文档中的 "nm" 键;
- _id 类型建议匹配实际存储类型:直接用 string 虽可工作(因 BSON 会尝试转换),但易丢失类型语义和校验能力;
- omitempty 仅影响序列化(如 Insert 或 Update 时忽略零值),对 Find 反序列化无影响,可按需保留。
修复后的完整示例(基于 mgo.v2):
package main
import (
"log"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
type StationConfig struct {
ID bson.ObjectId `bson:"_id,omitempty"`
Name string `bson:"nm"`
}
func main() {
session, err := mgo.Dial("192.168.1.41")
if err != nil {
panic(err)
}
defer session.Close()
c := session.DB("metos").C("stations")
var stationConfigs []StationConfig
err = c.Find(nil).Limit(5).All(&stationConfigs)
if err != nil {
log.Fatal(err)
}
log.Printf("Found %d stations: %+v", len(stationConfigs), stationConfigs)
// 输出示例:Found 5 stations: [{ID:ObjectIdHex("563c8d56819c3c91076b7c13") Name:"00000BE8"} ...]
}
总结:Go 的结构体序列化强依赖字段可见性。牢记 “小写=私有=不可反射” 这一原则,是避免 MongoDB(及 JSON/XML/ORM 等所有基于反射的序列化场景)空字段问题的根本前提。











