Search code examples
mongodbgobson

How to unmarshal a BSON string value into a custom type using go-mongo-driver?


I'm using MarshalBSONValue to marshal an inner struct field to a custom string representation.

I can't figure out how to implement the inverse operation, UnmarshalBSONValue, in order to parse the custom string representation into an inner struct.

import (
    "fmt"
    "testing"

    "github.com/stretchr/testify/assert"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/bsontype"
)

type inner struct {
    value string
}

func (i inner) MarshalBSONValue() (bsontype.Type, []byte, error) {
    return bsontype.String, []byte(i.value), nil
}

// How to implement this?
//
// func (i *inner) UnmarshalBSONValue(t bsontype.Type, data []byte) error {
//     ...
// }

type Outer struct {
    Inner inner `bson:"inner"`
}

func TestMarshalBSON(t *testing.T) {
    var outer1 = Outer{Inner: inner{value: "value"}}
    doc, err := bson.Marshal(outer1)
    assert.NoError(t, err)
    fmt.Printf("%#v\n", string(doc)) // "\x11\x00\x00\x00\x02inner\x00value\x00"

    var outer2 Outer
    err = bson.Unmarshal(doc, &outer2) // error
    assert.NoError(t, err)

    assert.Equal(t, outer1, outer2)
}

I would greatly appreciate it if anyone could provide a working implementation of UnmarshalBSONValue for the example test above.


Solution

  • You can use bsoncore.ReadString to parse the given value.

    func (i inner) MarshalBSONValue() (bsontype.Type, []byte, error) {
        return bson.MarshalValue(i.value)
    }
    
    func (i *inner) UnmarshalBSONValue(t bsontype.Type, value []byte) error {
        if t != bsontype.String {
            return fmt.Errorf("invalid bson value type '%s'", t.String())
        }
        s, _, ok := bsoncore.ReadString(value)
        if !ok {
            return fmt.Errorf("invalid bson string value")
        }
    
        i.value = s
        return nil
    }