Error when creating a custom mashler/unmashler for Bson.M mongodb
Asked Answered
A

2

5

I get the error WriteValueBytes can only write while positioned on a Element or Value but is positioned on a TopLevel when trying to create a custom mashler/unmashler for bson.M

I have a custom type called TransactionId which represents a UUID I want to convert this value into a string before storing to monbodb and Also convert it back from a string when pulling the value from mongodb.

This is the code I have so far

package main

import (
    "github.com/google/uuid"
    "github.com/pkg/errors"
    "go.mongodb.org/mongo-driver/bson/bsontype"
    "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    "go.mongodb.org/mongo-driver/bson"
    "log"
)

// TransactionID is a UUID used to trace a batch of work which is being processed.
type TransactionID uuid.UUID

// String returns the transaction id as a string
func (id TransactionID) String() (result string, err error) {
    val, err := uuid.FromBytes(id[:])
    if err != nil {
        return result, errors.Wrapf(err, "cannot convert transaction ID %s to UUID", string(id[:]))
    }
    return val.String(), nil
}

func (id TransactionID) MarshalBSONValue() (bsontype.Type, []byte, error) {
    idString, err :=  id.String()
    if err != nil {
        return bsontype.String, nil, err
    }
    return bsontype.String, bsoncore.AppendString(nil, idString), nil
}


func (id *TransactionID) UnmarshalBSONValue(bsonType bsontype.Type, bytes []byte) error {
    uid, err := uuid.FromBytes(bytes)
    if err != nil {
        return err
    }

    *id = TransactionID(uid)
    return nil
}


func NewTransactionID() TransactionID {
    return TransactionID(uuid.New())
}


func main() {
    id := NewTransactionID()

    _, err :=  bson.Marshal(id)
    if err != nil {
        log.Fatal(err)
    }
}

I'm getting the WriteValueBytes can only write while positioned on a Element or Value but is positioned on a TopLevel in the unmarshal step.

Link: https://play.golang.org/p/_n7VpX-KIyP

Assizes answered 17/4, 2020 at 18:0 Comment(0)
I
4

I'm getting the WriteValueBytes can only write while positioned on a Element or Value but is positioned on a TopLevel in the unmarshal step.

The function bson.Marshal() requires a parameter that can be transformed into a document (i.e. interface{}). This is why the error message is relating to the position of the value. i.e. you can't have a string on the top level of a document, it needs to be an element of a document. If you need to marshal a single value you should use bson.MarshalValue() instead.

id := NewTransactionID()
vtype, vdata, err := bson.MarshalValue(id)

An example to use bson.Marshal() is below (adding to your example):

type Foo struct {
    ID TransactionID
}

func main() {
    id := NewTransactionID()
    foo, err :=  bson.Marshal(&Foo{ID:id})
    if err != nil {
        panic(err)
    }
}
Imputation answered 20/4, 2020 at 3:37 Comment(1)
I'm trying and trying to understand this but can't get it to work. Here's a simpler example of the error, closer to what I'm working on: play.golang.org/p/z4Okvk9iPrS. In my case, the type I'm defining should have no problem being transformed to a document, no? I don't have loose strings or anything like that. What am I missing?Demonstration
K
0

fairlyFancyString is a document. it becomes a value when it implements the ValueMarshaler. bson.Marshal() needs a document.

Kaluga answered 30/1, 2023 at 10:9 Comment(1)
You can add code markdown styling using ` (backtiks) to your code parts of your content to increase readability of it.Ezzo

© 2022 - 2024 — McMap. All rights reserved.