
本文探讨了在使用 labix.org/v2/mgo 包与 MongoDB 交互时,bson.Unmarshal() 函数会清除结构体中未导出字段的现象。解释了其背后的设计原因,并提供了一些规避此行为的替代方案,帮助开发者在保持数据完整性的同时,有效地使用 mgo/bson 包。
在使用 mgo 包与 MongoDB 交互时,我们经常需要将从数据库中检索到的 BSON 数据解组 (Unmarshal) 到 Go 结构体中。然而,一个常见的问题是,bson.Unmarshal() 函数在解组过程中会将结构体中未导出的字段重置为其零值。这可能会导致我们期望保留的内部状态丢失。
问题根源
bson.Unmarshal() 的设计目标是确保解组的结果完全依赖于 BSON 数据本身,而不受结构体先前状态的影响。 为了实现这一点,bson.Unmarshal() 在填充字段之前,会显式地将结构体的所有字段(包括未导出的字段)设置为零值。 源码中体现了这一点,因此无法禁用此行为。
示例代码
以下代码演示了这个问题:
package mainimport ( "fmt" "labix.org/v2/mgo/bson")type Sub struct{ Int int }type Player struct { Name string unexpInt int unexpPoint *Sub}func main() { dta, err := bson.Marshal(bson.M{"name": "ANisus"}) if err != nil { panic(err) } p := &Player{unexpInt: 12, unexpPoint: &Sub{42}} fmt.Printf("Before: %+vn", p) err = bson.Unmarshal(dta, p) if err != nil { panic(err) } fmt.Printf("After: %+vn", p)}
输出结果:
Before: &{Name: unexpInt:12 unexpPoint:0x...}After: &{Name:ANisus unexpInt:0 unexpPoint:}
可以看到,在 bson.Unmarshal() 之后,unexpInt 和 unexpPoint 字段都被重置为零值。
规避方案
由于无法直接阻止 bson.Unmarshal() 清除未导出字段的行为,我们需要采用其他方法来解决这个问题。以下是一些可能的解决方案:
使用导出字段: 这是最直接的解决方案。如果未导出字段的状态需要在解组后保持不变,可以考虑将其导出。但是,这可能会改变结构体的 API,因此需要谨慎考虑。
BrandCrowd
一个在线Logo免费设计生成器
159 查看详情
解组到临时结构体: 创建一个只包含需要从 BSON 数据中解组的导出字段的临时结构体。将 BSON 数据解组到这个临时结构体中,然后手动将这些字段的值复制到原始结构体中。这样可以避免清除未导出字段。
package mainimport ( "fmt" "labix.org/v2/mgo/bson")type Sub struct{ Int int }type Player struct { Name string unexpInt int unexpPoint *Sub}type PlayerTemp struct { Name string `bson:"name"`}func main() { dta, err := bson.Marshal(bson.M{"name": "ANisus"}) if err != nil { panic(err) } p := &Player{unexpInt: 12, unexpPoint: &Sub{42}} fmt.Printf("Before: %+vn", p) // 解组到临时结构体 temp := &PlayerTemp{} err = bson.Unmarshal(dta, temp) if err != nil { panic(err) } // 手动复制字段 p.Name = temp.Name fmt.Printf("After: %+vn", p)}
输出结果:
Before: &{Name: unexpInt:12 unexpPoint:0x...}After: &{Name:ANisus unexpInt:12 unexpPoint:0x...}
可以看到,unexpInt 和 unexpPoint 字段的值在解组后仍然保持不变。
使用 bson.Raw 类型: 可以将整个 BSON 文档解组到 bson.Raw 类型中,然后使用 GetBson() 方法来提取特定的字段。这种方法需要手动处理数据类型转换,但可以完全控制解组过程。
使用其他序列化/反序列化库: 如果以上方法都不适用,可以考虑使用其他序列化/反序列化库,例如 encoding/json 或第三方库,它们可能提供更灵活的控制选项。
总结
bson.Unmarshal() 清除未导出字段的行为是其设计的一部分,目的是确保解组结果的确定性。虽然无法直接禁用此行为,但我们可以通过使用导出字段、解组到临时结构体、使用 bson.Raw 类型或使用其他序列化/反序列化库等方法来规避这个问题。选择哪种方法取决于具体的应用场景和需求。 在选择方案时,需要权衡代码的复杂性、性能和可维护性。
以上就是防止 mgo/bson 解组时清除未导出字段:原理与规避方案的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1132539.html
微信扫一扫
支付宝扫一扫