Skip to content

Instantly share code, notes, and snippets.

@divjotarora
Created November 21, 2019 18:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save divjotarora/5bc3b6f85ef1030df6db83648c91dea6 to your computer and use it in GitHub Desktop.
Save divjotarora/5bc3b6f85ef1030df6db83648c91dea6 to your computer and use it in GitHub Desktop.
bson.MarshalValue proposal
package main
import (
"fmt"
"log"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsontype"
)
// Inner represents a union type. Either Foo or Bar will be a non-empty slice, but not both.
type Inner struct {
Foo []int
Bar []string
}
var _ bson.ValueMarshaler = Inner{}
var _ bson.ValueUnmarshaler = (*Inner)(nil)
// MarshalBSONValue marshals returns a BSON array representing either i.Foo or i.Bar
func (i Inner) MarshalBSONValue() (bsontype.Type, []byte, error) {
if len(i.Foo) > 0 {
// marshal as a temporary struct type and only pull out the foo field
type InnerTemp struct {
Foo []int
}
res, err := bson.Marshal(InnerTemp{
Foo: i.Foo,
})
if err != nil {
return 0, nil, err
}
return bsontype.Array, bson.Raw(res).Lookup("foo").Array(), nil
}
// marshal as a temporary struct type and only pull out the bar field
type InnerTemp struct {
Bar []string
}
res, err := bson.Marshal(InnerTemp{
Bar: i.Bar,
})
if err != nil {
return 0, nil, err
}
return bsontype.Array, bson.Raw(res).Lookup("bar").Array(), nil
}
// what an implementation of MarshalBSONValue could look like using the proposed bson.MarshalValue function:
// func (i Inner) MarshalBSONValue() (bsontype.Type, []byte, error) {
// if len(i.Foo) > 0 {
// return bson.MarshalValue(i.Foo)
// }
// return bson.MarshalValue(i.Bar)
// }
// UnmarshalBSONValue unmarshals value as either i.Foo or i.Bar
func (i *Inner) UnmarshalBSONValue(bt bsontype.Type, value []byte) error {
rawValue := bson.RawValue{
Type: bt,
Value: value,
}
// try unmarshalling as Foo
if err := rawValue.Unmarshal(&i.Foo); err == nil {
return nil
}
// try unmarshalling as Bar
return rawValue.Unmarshal(&i.Bar)
}
// Outer wraps Inner
type Outer struct {
I Inner
}
func main() {
// We want the documents for outer to look like
// {i: [1, 2]} or {i: ["hello", "world"]}
// rather than
// {i: {foo: [1, 2]}} or {i: {bar: ["hello", "world"]}}
outer := Outer{
I: Inner{
Foo: []int{1, 2, 3},
// Bar: []string{"hello", "world"},
},
}
res, err := bson.Marshal(outer)
if err != nil {
log.Fatal(err)
}
fmt.Printf("marshalled: %s\n", bson.Raw(res))
var unmarshalled Outer
if err := bson.Unmarshal(res, &unmarshalled); err != nil {
log.Fatal(err)
}
fmt.Printf("unmarshalled: %+v\n", unmarshalled)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment