Golang driver cannot use byte array as map key

I’m trying to marshal/unmarshal a map to bson in which the key type is a byte array:

package main

import (
	"fmt"
  "go.mongodb.org/mongo-driver/bson"
)

type myByteMap map[[2]byte]string

func main() {
	data := myByteMap{
		[2]byte{0, 0}: "Hello",
	}
  binData, err := bson.Marshal(data)
  if err != nil {
    panic(err)
  }
  fmt.Println(binData)
}

The error:

panic: unsupported key type: [2]uint8

The current driver only supports string as the map key. Is it a bug or designed as such? Any suggestions on the workaround?

Edit:

Tested with the following map:

map[string] OK
map[int] OK
map[byte] OK
map[[2]int] FAIL
map[[2]byte] FAIL

Hey @MoongooDB, thanks for the question! I believe that behavior is intentional and is partially a limitation of the BSON format specification, which requires all document keys to be strings (Go map types are encoded as nested documents). The Go BSON marshaler doesn’t define a standard way to convert a [2]byte to a string, so the Marshal call fails. However, you can define that conversion yourself by making your map key type implement the bsoncodec.KeyMarshaler interface:

type KeyMarshaler interface {
	MarshalKey() (key string, err error)
}

Here’s an example starting with your code sample (check out a working version here):

package main

import (
	"encoding/hex"
	"fmt"

	"go.mongodb.org/mongo-driver/bson"
)

type myByteMapKey [2]byte

func (key myByteMapKey) MarshalKey() (string, error) {
	return hex.EncodeToString(key[:]), nil
}

type myByteMap map[myByteMapKey]string

func main() {
	data := myByteMap{
		myByteMapKey{0, 0}: "Hello",
	}
	binData, err := bson.Marshal(data)
	if err != nil {
		panic(err)
	}
	fmt.Println(binData)
}

To unmarshal the output, you’d then have to implement the bsoncodec.KeyUnmarshaler interface for the myByteMapKey type.

2 Likes

Thanks for the solution above, and I would like to stress one thing

type myByteMapKey [2]byte

func (key myByteMapKey) MarshalKey() (string, error) {
	return hex.EncodeToString(key[:]), nil
}

Codes above work, but bson’s map codec does not allow the following codes:

type myByteMapKey [2]byte

func (key *myByteMapKey) MarshalKey() (string, error) {
	return hex.EncodeToString(key[:]), nil
}

That’s because bson map codec does not allow pointer in the mongo-go-driver/bson/bsoncodec/map_codec.go:

// KeyMarshalers are marshaled
if km, ok := val.Interface().(KeyMarshaler); ok {
	if val.Kind() == reflect.Ptr && val.IsNil() {
		return "", nil
	}
	buf, err := km.MarshalKey()
	if err == nil {
		return buf, nil
	}
	return "", err
}

Hope this hint could be helpful.

@xunjie_liu that’s expected behavior. Your example with the *myByteMapKey receiver for MarshalKey() doesn’t work because the encoded key type and the method receiver type must match exactly. The pointer check in the code you linked only disallows nil pointers.

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.