18

What I am looking is equivalent of Document.parse()

in golang, that allows me create bson from json directly? I do not want to create intermediate Go structs for marshaling

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Ganesh
  • 285
  • 1
  • 3
  • 10
  • You can choose either you will use some intermediate structs for marshalling/unmarshalling (high level interface) or directly bson, or low level like bson.M, bson.D. Look at http://stackoverflow.com/questions/25518297/golang-bson-conversion – lofcek Sep 30 '16 at 07:21

5 Answers5

20

The gopkg.in/mgo.v2/bson package has a function called UnmarshalJSON which does exactly what you want.

The data parameter should hold you JSON string as []byte value.

 func UnmarshalJSON(data []byte, value interface{}) error

UnmarshalJSON unmarshals a JSON value that may hold non-standard syntax as defined in BSON's extended JSON specification.

Example:

var bdoc interface{}
err = bson.UnmarshalJSON([]byte(`{"id": 1,"name": "A green door","price": 12.50,"tags": ["home", "green"]}`),&bdoc)
if err != nil {
    panic(err)
}
err = c.Insert(&bdoc)

if err != nil {
    panic(err)
}
Ganesh
  • 285
  • 1
  • 3
  • 10
TheHippo
  • 61,720
  • 15
  • 75
  • 100
  • What about the second parameter value, the documentation doesn't seem to elaborate what type it needs to be? From, my understanding of go, interface{} is equivalent of void * in C or Object in java ? Pointer to an unmarshalJSON example would be even better – Ganesh Sep 30 '16 at 17:46
  • There is big difference between void* and interface{}. When variable is void* there is no way to find out the type of variable. Whereas interface{} knows the type. – lofcek Sep 30 '16 at 19:54
9

mongo-go-driver has a function bson.UnmarshalExtJSON that does the job.

Here's the example:

var doc interface{}
err := bson.UnmarshalExtJSON([]byte(`{"foo":"bar"}`), true, &doc)
if err != nil {
    // handle error
}
Dennis Gloss
  • 2,307
  • 4
  • 21
  • 27
  • Note here that `doc` will be in `ExtendedJson` which may restructure the input to include the mongo bson type system. – dustinevan Nov 16 '22 at 20:03
1

There is no longer a way to do this directly with supported libraries (e.g. the mongo-go-driver). You would need to write your own converter based on the bson spec.

Edit: here's one that by now has seen a few Terabytes of use in prod. https://github.com/dustinevan/mongo/blob/main/bsoncv/bsoncv.go

dustinevan
  • 918
  • 9
  • 21
1

I do not want to create intermediate Go structs for marshaling

If you do want/need to create an intermediate Go BSON structs, you could use a conversion module such github.com/sindbach/json-to-bson-go. For example:

import (
    "fmt"
    "github.com/sindbach/json-to-bson-go/convert"
    "github.com/sindbach/json-to-bson-go/options"
)

func main() {
    doc := `{"foo": "buildfest", "bar": {"$numberDecimal":"2021"} }`
    opt := options.NewOptions()
    result, _ := convert.Convert([]byte(doc), opt)
    fmt.Println(result)
}

Will produce output:

package main

import "go.mongodb.org/mongo-driver/bson/primitive"

type Example struct {
    Foo string               `bson:"foo"`
    Bar primitive.Decimal128 `bson:"bar"`
}

This module is compatible with the official MongoDB Go driver, and as you can see it supports Extended JSON formats.

You can also visit https://json-to-bson-map.netlify.app to try the module in action. You can paste a JSON document, and see the Go BSON structs as output.

Wan B.
  • 18,367
  • 4
  • 54
  • 71
1

A simple converter that uses go.mongodb.org/mongo-driver/bson/bsonrw:

func JsonToBson(message []byte) ([]byte, error) {
    reader, err := bsonrw.NewExtJSONValueReader(bytes.NewReader(message), true)
    if err != nil {
        return []byte{}, err
    }
    buf := &bytes.Buffer{}
    writer, _ := bsonrw.NewBSONValueWriter(buf)
    err = bsonrw.Copier{}.CopyDocument(writer, reader)
    if err != nil {
        return []byte{}, err
    }
    marshaled := buf.Bytes()
    return marshaled, nil
}
Sergey Ponomarev
  • 2,947
  • 1
  • 33
  • 43