Reputation: 63
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
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.
Upvotes: 3
Views: 4470
Reputation: 1430
for anyone who has a deep level of nesting of the structs, it is better to change the nested data's type from interface{}
to bson.M
. For example:
type A struct {
Data interface{} `json:"data,omitempty"`
}
type B struct {
Datas []A `json:"datas,omitempty"`
}
type C struct {
Bdata *B `json:"bData,omitempty"`
}
change the A struct to
type A struct {
Data bson.M `json:"data,omitempty"`
}
Now the decoding from mongo results should be of the correct format. You may have to do additional type casting yourself to work with the data afterwards unfortunately.
Upvotes: 0
Reputation: 21
build a register :
register := bson.NewRegistryBuilder().
RegisterTypeMapEntry(bsontype.EmbeddedDocument, reflect.TypeOf(primitive.M{})).
Build()
and set it to options :
options.Client().SetRegistry(register)
Upvotes: 2
Reputation: 1
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.
Upvotes: 0
Reputation: 18430
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)
Upvotes: 6
Reputation: 311
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.
Upvotes: 0