How to marshal json string to bson document for writing to MongoDB?
Asked Answered
A

5

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

Aberrant answered 30/9, 2016 at 6:45 Comment(1)
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 https://mcmap.net/q/670191/-golang-bson-conversionCarper
D
21

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)
}
Duron answered 30/9, 2016 at 9:52 Comment(2)
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 betterAberrant
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.Carper
D
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
}
Delldella answered 5/1, 2021 at 20:34 Comment(1)
Note here that doc will be in ExtendedJson which may restructure the input to include the mongo bson type system.Aaberg
A
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

Aaberg answered 27/12, 2020 at 18:49 Comment(0)
M
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.

Mcnew answered 15/3, 2021 at 0:56 Comment(0)
U
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
}
Uprear answered 26/8, 2022 at 19:46 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.