4

I am working with MongoDB in my Go application, and pull some data out and store it in a bson.M (which . Here is an example, of getting the bson.M object from the DB and then printing it (let's call this object data):

[map[_id:ObjectID("XXXXXXX") address:XX decimal:18 providers:map[currency:value] symbol:LINK]

Which looks correct to me.

I think want to loop over the map in the providers field (as you can see, it's a map in there as well). I've done a few attempts, but each time I am blocked.

Due to what I've read in the docs here and the testing I've done, it looks like a bson.M and primitive.M are the same, and they are each treated as a map[string]interface{}.

When I attempted to assert it to a map[string]string I go a panic error:

// code run
data["providers"].(map[string]string)

//error received
panic: interface conversion: interface {} is primitive.M, not map[string]string

The reason for this, is I want to loop over the providers field, and when I try to loop as-is, I get this error:

// code to run
for key, provider := range data["providers"] {...}

// error received
cannot range over data["providers"] (map index expression of type interface{})

I have read that I might need to do something with marshaling and decoding, but I feel like I'm just missing a step as to why I would need to do those, or how they would help.

In any case to summarize:

  1. How does one loop over a primitive.M/bson.M/map[string]interface{}?

  2. How does one convert a primitive.M/bson.M/map[string]interface{} to a map[string]string?

It looks like I might be trying to do the opposite of this entry, and it looks like this entry is giving me conflicting information. Hoping to edit the question as I understand more what my real problem is. Thank you!

Patrick Collins
  • 5,621
  • 3
  • 26
  • 64

2 Answers2

6

Speaking with Burak Serdar helped give us the answer! You have to assert the data["providers"] to a primitive.M

for key, provider := range data["providers"].(primitive.M){...}

Key thing to note: primitive.M is handled like a map[string]interface{}

As Burak Serdar mentioned, I cannot assert this instance of a primitive.M to a map[string]string, because the interface in map[string]interface{} is of type primitive.M. So instead, I have to assert the providers object to a primitive.M object, and then I can loop over it normally.

I cannot convert a primitive.M to a map[string]string

Patrick Collins
  • 5,621
  • 3
  • 26
  • 64
3

A type assertion for map[string]interface{} to map[string]string will fail, because it is a type assertion, not type conversion. It only checks if the interface type is what you think it is, and in this case, it is not. You have to iterate the map[string]interface{}, and type-assert the values:

for k,v:=range data.(primitive.M)["providers"] {
   if str, ok:=v.(string); ok {
      // Use k and str
   }
}
Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
  • It doesn't reach the "if" statement when I tried that, I still get `cannot range over data["providers"] (type interface {})`. Should I be converting data to something else before running the loop? – Patrick Collins Apr 21 '20 at 23:51
  • 1
    Is data primitive.M? If so, try the edited version above – Burak Serdar Apr 21 '20 at 23:52
  • Ahh that almost worked! So it turns out you have to do `data["providers"].(primitive.M)` (close to what you had) and then you don't have to type-assert the values, although I'm not sure why. I THINK it's because the providers object inside the data object can be asserted to a primitive.M and not a map, since that's what it's an interface of. I'll add an answer to the question to clarify. Thank you for your help! – Patrick Collins Apr 22 '20 at 00:00