Я изо всех сил пытаюсь маршалировать / демаршалировать массив bson с полиморфной структурой с помощью mongo-go-driver。 Я планирую сохранить структурный дискриминатор в маршалированные данные и написать пользовательскую функцию UnmarshalBSONValue, чтобы декодировать ее в соответствии с структурным дискриминатором. Но я не знаю, как это правильно делать.
package polymorphism
import (
"fmt"
"testing"
"code.byted.org/gopkg/pkg/testing/assert"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsontype"
)
type INode interface {
GetName() string
}
type TypedNode struct {
NodeClass string
}
type Node struct {
TypedNode `bson:"inline"`
Name string
Children INodeList
}
func (n *Node) GetName() string {
return n.Name
}
type INodeList []INode
func (l *INodeList) UnmarshalBSONValue(t bsontype.Type, data []byte) error {
fmt.Println("INodeList.UnmarshalBSONValue")
var arr []bson.Raw // 1. First, try to decode data as []bson.Raw
err := bson.Unmarshal(data, &arr) // error: cannot decode document into []bson.Raw
if err != nil {
fmt.Println(err)
return err
}
for _, item := range arr { // 2. Then, try to decode each bson.Raw as concrete Node according to `nodeclass`
class := item.Lookup("nodeclass").StringValue()
fmt.Printf("class: %v\n", class)
if class == "SubNode1" {
bin, err := bson.Marshal(item)
if err != nil {
return err
}
var sub1 SubNode1
err = bson.Unmarshal(bin, &sub1)
if err != nil {
return err
}
*l = append(*l, &sub1)
} else if class == "SubNode2" {
//...
}
}
return nil
}
type SubNode1 struct {
*Node `bson:"inline"`
FirstName string
LastName string
}
type SubNode2 struct {
*Node `bson:"inline"`
Extra string
}
С помощью приведенного выше кода я пытаюсь декодировать данные INodeList
как []bson.Raw
, а затем декодировать каждый bson.Raw
как конкретный узел в соответствии с nodeclass
. Но он сообщает об ошибке:
cannot decode document into []bson.Raw
на линии
err := bson.Unmarshal(data, &arr)
.
Итак, как это сделать правильно?