基于已知密钥使用 UnmarshalBSON 解组动态接口

问题描述 投票:0回答:1

我有一个类型为 foo 的对象,其中包含一个接口 ActivationInterface ;该对象保存在 MongoDB 中,由于内部对象的基础类型未知,我无法将其取回。

我按如下方式实现了 UnmarshalBSON 但没有成功,因为即使在设置了接口的具体类型之后,解组器现在仍然执行底层类型,因为我仍然收到错误: 解码关键行为错误:找不到 main.ActivationInterface 的解码器

你知道我怎样才能实现这个目标吗?

我在这里发现了一些接近工作的东西,所以我不明白为什么我的不是:基于类型键解组动态 JSON 我看不出我做错了什么,有什么不同......!

编辑:我更新了代码以与 json 进行比较。 UnmarshalJSON 使用完全相同的代码工作得很好,而 UnmarshalBSON 仍然失败。

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)
}


去游乐场:https://go.dev/play/p/bHMy6-ZLsYQ
几乎相同的代码,但使用 JSON 并工作:https://go.dev/play/p/V5HLrQ_-ls3

谢谢!

go unmarshalling mongo-go-driver
1个回答
1
投票

在结构中使用接口时,解组无法确定要选择哪个“实现”...您必须根据“类型”字段手动执行此操作。通常,unmarshall 方法会放置一个接口{} 的映射,即键值存储。 无论如何,回到你的问题,你必须将接口数据存储在 bson.Raw (字节片)中,并通过选择正确的结构手动进行解组。

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"`
        Act  bson.Raw
    })
    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{}
        a := Activation1{}
        err := bson.Unmarshal(fooTemp.Act, &a)
        if err != nil {
            return err
        }
        q.Act = a
    case "act2":
        // q.Act = &Activation2{}
        a := Activation2{}
        err := bson.Unmarshal(fooTemp.Act, &a)
        if err != nil {
            return err
        }
        q.Act = a
    default:
        fmt.Println("DEFAULT")
        return fmt.Errorf("unknown type: %v", fooTemp.Type)
    }

    return nil
}

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)
}

https://go.dev/play/p/CG2SlEknNrO

© www.soinside.com 2019 - 2024. All rights reserved.