Go 语言本身是强类型, 这不利于处理联合体(union).

在使用 mgo 的过程中, 经常会遇到类似的场景: 某个字段的值可能是类型A, 也可能是类型B.

为了达到上述的目的, 需要自定义 bson.Marshal & bson.Unmarsahl 方法: 实现 bson.Getter & bson.Setter interface

完整的示例的代码: https://play.golang.org/p/ESDlrW353is

自定义 bson.Marshal & bson.Unmarshal

// 示例结构体
type T struct {
	ID   string
	Elem Elem
}

需求: 存储 T 类型字段到 mongo 时, 如果 ID 字段有值, 则存入 ID, 否则存入 Elem 字段.

bson.Marshal: GetBSON

实现 bson.Getter interface, 自定义字段序列化.

// GetBSON bson marshal
func (t T) GetBSON() (interface{}, error) {
	if t.ID != "" {
		return t.ID, nil
	} else {
		return t.Elem, nil
	}
}

bson.Unmarshal: SetBSON

实现 bson.Setter interface, 自定义字段反序列化.

// SetBSON bson unmarshal
func (t *T) SetBSON(raw bson.Raw) error {
	if err := raw.Unmarshal(&t.Elem); err == nil {
		return nil
	}

	return raw.Unmarshal(&t.ID)
}

自定义 json.Marshal & json.Unmarshal

结构体 json 序列化后等价于序列化其中一种有效字段.

json.Marshal: MarshalJSON

// MarshalJSON custom json.Unmarshal
func (t T) MarshalJSON() ([]byte, error) {
	if t.ID != "" {
		return json.Marshal(t.ID)
	} else {
		return json.Marshal(t.Elem)
	}
}

json.Unmarshal: UnmarshalJSON

// UnmarshalJSON custom json.Marshal
func (t *T) UnmarshalJSON(data []byte) error {
	if data[0] == '"' {
		return json.Unmarshal(data, &t.ID)
	} else {
		return json.Unmarshal(data, &t.Elem)
	}
}

参考资料