2

Using gopkg.in/mgo.v2/bson, I wonder how to marshal an interface{} value into a value of type bson.Raw.

The documentation for bson.Raw states:

Using this type it is possible to unmarshal or marshal values partially.

But I can't find a Marshal function that would return bson.Raw.

What am I missing?

Example of what I try to do:

package main

import (
    "fmt"

    "gopkg.in/mgo.v2/bson"
)

func main() {
    // How to avoid a MarshalRaw help function?
    raw, err := MarshalRaw("Hello world")
    if err != nil {
        panic(err)
    }

    fmt.Printf("%+v\n", raw)
}

func MarshalRaw(v interface{}) (*bson.Raw, error) {
    bin, err := bson.Marshal(struct{ Raw interface{} }{v})
    if err != nil {
        return nil, err
    }

    var raw struct{ Raw bson.Raw }
    err = bson.Unmarshal(bin, &raw)
    if err != nil {
        return nil, err
    }

    return &raw.Raw, nil
}

Output:

&{Kind:2 Data:[12 0 0 0 72 101 108 108 111 32 119 111 114 108 100 0]}

ANisus
  • 74,460
  • 29
  • 162
  • 158

3 Answers3

3

bson.Raw is used as a value both when marshaling and unmarshaling.

To transform an interface{} into a bson.Raw, the first thing to do is to marshal it so that you get the plain document data that represents whatever is being marshaled:

    var value interface{} = bson.M{"some": "value"}
    data, err := bson.Marshal(value)
    if err != nil {
            log.Fatal(err)
    }

and then it may have one or more fields unmarshaled into bson.Raw values:

    var doc struct{ Some bson.Raw }
    err = bson.Unmarshal(data, &doc)
    if err != nil {
            log.Fatal(err)
    }

or even the entire document:

    var doc bson.Raw
    err = bson.Unmarshal(data, &doc)
    if err != nil {
            log.Fatal(err)
    }

If you want the entire document rather than just a field, you can also use this shortcut:

    doc := bson.Raw{3, data}

The 3 constant represents a document in the bson specification, and it must of course match the provided data. Since BSON only supports documents at the top level, we know this must be correct.

Gustavo Niemeyer
  • 22,007
  • 5
  • 57
  • 46
2

I believe that bson.Raw is intended to be used as a type for a variable.

for example: (in play)

type Bar struct {
   AnInt int
   AString bson.Raw
}

The "AString" field will be kept as the bson.Raw struct your link mentions.

This is super-useful if you want to partially decode the top level of a nested structure to figure out its actualy type so you can parse the rest in to the proper datatype.

Note, the above is untested, not in front of a machine I can actually run go on at the moment. This is based on the assumption that it works like the standard encoding/json package.

David Budworth
  • 11,248
  • 1
  • 36
  • 45
  • Yes, it is also my intention and covers the partial *unmarshalling* of the struct. But when you want to Insert a `Bar` value into the database, you want to set the `AString` value. How to create this `bson.Raw` value from a normal string (or any other type)? After all, the documentation mentions "marshal values partially", and the Playground solution of using a separate struct, `Foo`, to do the marshalling is not "partial". – ANisus Aug 13 '14 at 03:07
  • I've added an example of a workaround. It is not pretty though. – ANisus Aug 13 '14 at 03:51
1

Solution I was looking for

m := bson.M{"ns": bson.M{"coll": "test1", "db": "testdb"}}
data, _ := bson.Marshal(m)
r := bson.Raw(data)
coll := r.Lookup("ns", "coll").StringValue()
println(coll)

Playground

okharch
  • 387
  • 2
  • 10