I have an object of type foo containing an interface ActivationInterface ; this object is saved in MongoDB and I have trouble fetching it back as the underlying type of the inner object is not known.
I implemented UnmarshalBSON as follow without success, as it seems even after setting the concrete type of the interface, the unmarshaller still does now the underlying type, as I still get the error: error decoding key act: no decoder found for main.ActivationInterface
Do you have any idea how I can achieve this ?
I found somethign close woking here, so I don't get why mine isn't: Unmarshal dynamic JSON based on a type key I cannot see what I'm doing wrong and different...!
EDIT: I updated the code to compare with json. UnmarshalJSON is working great with exactly the same code while UnmarshalBSON still fails.
package main
import (
"fmt"
"log"
"go.mongodb.org/mongo-driver/bson"
)
type foo struct {
Type string `bson:"type"`
Act ActivationInterface
}
type ActivationInterface interface{}
type Activation1 struct {
Name string `bson:"name"`
}
type Activation2 struct {
Address string `bson:"adress"`
}
func (q *foo) UnmarshalBSON(data []byte) error {
// Unmarshall only the type
fooTemp := new(struct {
Type string `bson:"type"`
})
if err := bson.Unmarshal(data, fooTemp); err != nil {
return err
}
fmt.Println(fooTemp.Type)
// Set the type to the prop
switch fooTemp.Type {
case "act1":
// q.Act = &Activation1{}
q.Act = new(Activation1)
case "act2":
// q.Act = &Activation2{}
q.Act = new(Activation2)
default:
fmt.Println("DEFAULT")
}
// Call Unmarshal again
type Alias foo // avoids infinite recursion using a type alias
return bson.Unmarshal(data, (*Alias)(q))
}
func main() {
foo1 := foo{
Type: "act1",
Act: Activation1{
Name: "name: act1",
},
}
foo2 := foo{
Type: "act2",
Act: Activation2{
Address: "adress: act2",
},
}
// Marshal
m1, err := bson.Marshal(foo1)
if err != nil {
log.Fatal(err)
}
m2, err := bson.Marshal(foo2)
if err != nil {
log.Fatal(err)
}
//fmt.Println(m1, m2)
// Unmarshal
var u1, u2 foo
err = bson.Unmarshal(m1, &u1)
if err != nil {
fmt.Println("1 -> ", err) // error decoding key act: no decoder found for main.ActivationInterface
}
err = bson.Unmarshal(m2, &u2)
if err != nil {
fmt.Println("2 -> ", err) // error decoding key act: no decoder found for main.ActivationInterface
}
fmt.Println(foo1.Type, ":", u1.Act.(*Activation1).Name)
fmt.Println(foo2.Type, ":", u2.Act.(*Activation2).Address)
}
Go playground: https://go.dev/play/p/bHMy6-ZLsYQ
Almost the same code, but using JSON and working: https://go.dev/play/p/V5HLrQ_-ls3
Thanks !
When using an interface in your struct, unmarshall cannot figure out which "implementation" to choose... You have to do it manually, in your case based on the "type" field. Usually unmarshall methods put a map for interface{}, a key value store. Anyway back on your problem, you have to store the interface data in bson.Raw (slice of bytes) and do the unmarshalling manually by choosing the right struct.
https://go.dev/play/p/CG2SlEknNrO