Go中如何对支持两种格式的通用JSON&BSON字段做Marshal/Unmarshal处理
解决方案
针对Go中Mongo集合多态字段的映射问题,有两种常用实现方案:
方案1:使用any类型快速适配
适合业务逻辑简单、不需要强类型校验的场景,直接用空接口接收Content字段,后续根据Type字段做类型断言:
import "go.mongodb.org/mongo-driver/bson" type C struct { Type string `json:"type" bson:"type"` Content any `json:"content" bson:"content"` // inline标签将嵌入结构体的字段展开到父结构体层级,匹配Mongo存储结构 *TextC `bson:",inline"` *GroupC `bson:",inline"` } type TextC struct { ExtraTextData string `json:"extraTextData" bson:"extraTextData"` } type GroupC struct { ExtraGroupData string `json:"extraGroupData" bson:"extraGroupData"` }
使用时的判断逻辑:
var doc C // 此处省略Mongo查询解码到doc的逻辑 switch doc.Type { case "text": contentStr, ok := doc.Content.(string) if !ok { // 处理格式异常逻辑 } // 正常使用contentStr和doc.ExtraTextData case "group": contentArr, ok := doc.Content.(bson.A) if !ok { // 处理格式异常逻辑 } // 将bson.A转为[]C后即可正常使用,同时可读取doc.ExtraGroupData }
方案2:自定义Content类型实现BSON编解码接口
适合业务复杂、需要类型安全的场景,通过实现Mongo驱动的编解码接口,自动完成两种格式的适配,避免运行时断言错误:
步骤1:定义自定义Content类型并实现编解码接口
import ( "fmt" "go.mongodb.org/mongo-driver/bson" ) type Content struct { StrVal string // 存储text类型的字符串内容 ArrVal []C // 存储group类型的数组内容 IsArray bool // 标记当前存储的内容类型 } // 实现BSON解码接口 func (c *Content) UnmarshalBSONValue(typ bson.Type, b []byte) error { switch typ { case bson.TypeString: if err := bson.UnmarshalValue(typ, b, &c.StrVal); err != nil { return err } c.IsArray = false return nil case bson.TypeArray: if err := bson.UnmarshalValue(typ, b, &c.ArrVal); err != nil { return err } c.IsArray = true return nil default: return fmt.Errorf("不支持的Content类型: %v", typ) } } // 实现BSON编码接口(需要回写Mongo时才需要实现) func (c Content) MarshalBSONValue() (bson.Type, []byte, error) { if c.IsArray { return bson.MarshalValue(c.ArrVal) } return bson.MarshalValue(c.StrVal) }
步骤2:调整主结构体定义
type C struct { Type string `json:"type" bson:"type"` Content Content `json:"content" bson:"content"` *TextC `bson:",inline"` *GroupC `bson:",inline"` } type TextC struct { ExtraTextData string `json:"extraTextData" bson:"extraTextData"` } type GroupC struct { ExtraGroupData string `json:"extraGroupData" bson:"extraGroupData"` }
步骤3:使用示例
var doc C // 此处省略Mongo查询解码到doc的逻辑 switch doc.Type { case "text": fmt.Println("文本内容:", doc.Content.StrVal) fmt.Println("文本额外字段:", doc.ExtraTextData) case "group": fmt.Println("分组子元素数量:", len(doc.Content.ArrVal)) fmt.Println("分组额外字段:", doc.ExtraGroupData) }
补充说明
如果需要同时支持JSON格式的编解码,只需要给Content类型额外实现json.Marshaler和json.Unmarshaler接口,逻辑和BSON编解码完全一致即可。
内容的提问来源于stack exchange,提问作者sameerd
相关产品推荐
相关产品推荐

