简体   繁体   中英

How to unmarshal bson document as type M with mongo-go-driver

According to the documentation in mongo-driver https://godoc.org/go.mongodb.org/mongo-driver/bson Unmarshalling bson into interface{} will default the value into D

  1. When unmarshalling, a field of type interface{} will follow the D/M type mappings listed above. BSON documents unmarshalled into an interface{} field will be unmarshalled as a D.

If I store an object in db as:

"data": {
   "property1": "value1",
   "property2": "value2",
}

When I retrieve this data with"

var result interface{}
err = collection.FindOne(ctx, filter).Decode(&result)

it becomes:

"data": [
   {"Key": "property1", "Value": "value1"},
   {"Key": "property2", "Value": "value2"}
]

Which match the documentation, type D is a slice of key value

My question is, is there a way to change this behavior so that it will unmarshal the data into M?

My app is not aware of the actual structure of the data. It simply takes the json as input and store in db so I have to use interface{} as the type when unmarhsal. If it stores in db as a map, it should retrieve the data in same way.

It's default feature if you pass interface{} then unmarshal into bson.D which can't be changed. But you can convert from bson.D to bson.M this way.

resultMap := result.(bson.D).Map()

Or you can use a variable of bson.M directly when unmarshal.

var result bson.M
err = collection.FindOne(ctx, filter).Decode(&result)

Unmarshal to a variable of type bson.M :

var result bson.M
err = collection.FindOne(ctx, filter).Decode(&result)

Becaue the bson.M type satisfies the interface interface{} (as do all types), you can use the bson.M value anywhere you used the interface{} value.

Run it on the playground .

I have run into that issue when working with opa library. For some reason, opa module was not returning proper evaluation for nested json values. Accoring to: https://www.mongodb.com/community/forums/t/storing-deeply-nested-data/9985/3

The bson.D type is internally represented as a slice to structs with fields named Key and Value (...) Unlike bson.D, the bson.M type is simply map[string]interface{} so the standard library json functions can handle it.

I have started diving deeper into this. As someone mentioned it earlier, the default format for bson unmarshal is bson.D type. When we unmarshal like this, we get: unmarshaling into interface{}

But when we enforce to unmarshal into bson.M, instead of just interface{} or bson.D we get: unmarshaling into bson.M

We can convert bson.D to bson.M using.Map method, but in the end it is not the same: bson.D converted to bson.M

The types of nested field "baz" is different in both scenarios.

This explains the current implementation of.Map method, which currently looks like this:

func (d D) Map() M {
    m := make(M, len(d))
    for _, e := range d {
        m[e.Key] = e.Value
    }
    return m
}

It looks like convertion bson.D to bson.M using.Map method is not the same as directly unmarshaling to bson.M.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM