-
-
Save divjotarora/5bc3b6f85ef1030df6db83648c91dea6 to your computer and use it in GitHub Desktop.
bson.MarshalValue proposal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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