【问题标题】:How do I use bson.SetBSON or bson.Raw?如何使用 bson.SetBSON 或 bson.Raw?
【发布时间】:2020-04-03 05:40:11
【问题描述】:

可能相关:How to use interface type as a model in mgo (Go)?

我有一个这样的结构:

type Game struct {
    ID       bson.ObjectId
    Type     string
    Location string
    Details  interface{}
}

type FeudDetails struct {
    ...
}

type TriviaDetails struct {
    ...
}

type BingoDetails struct {
    ...
}

我想使用Game 的Type 字段将Details 反序列化为特定类型(如FeudDetails 或BingoDetails 的实例)。在Game 中它仍然是interface{},但我可以这样做:

feudDetails, ok := game.Details.(FeudDetails)
if ok {
    // we know this is a Feud game, and we have the details
    feudDetails.Round++
}

The docs 说“可以使用 bson.Raw 部分解组或编组值”,但他们没有提供我能找到的任何示例。

我尝试过使用:

func (game *Game) SetBSON(r bson.Raw) error {
    err := r.Unserialize(game)
    if err != nil {
        return nil
    }
    games[game.Type].LoadDetails(game)  // this calls a function based on the Type to
                                        // create a concrete value for that game.
    return nil
}

我在这里得到一个 (ahem) 堆栈溢出。我认为这是因为r.Unserialize 递归调用SetBSON。

我的目标是对所有字段除了 Details 使用标准反序列化,然后能够使用game.Type 来确定如何处理Details。如果我这样做:

type GameDetails interface{}

type Game struct {
    ...
    Details GameDetails
}

func (details *GameDetails) SetBSON(r bson.Raw) error {
    // game isn't defined
    games[game.Type].LoadDetails(r, details)
}

那么我如何访问外部Type 字段以了解将其反序列化为哪种游戏类型?

我也会接受“你做错了,Go 中一个更好的模式是 XYZ...”的答案。

编辑:我也尝试正常反序列化,然后使用game.Details.(FeudDetails) 转换interface{} 的interface{} 版本,但转换失败。我想我不能那样做,因为反序列化后的底层类型不是FeudDetails,而可能是map[string]interface{}。

编辑 2: 我认为我会很聪明,并在从数据库中检索时使用正确的类型预先填充一个对象(在调用 db...One(&game) 之前),但我的诡计确实做到了不工作:

DEBU[Mar 31 22:19:09.442] Caching show                 gid=5e814448ef5b9858b7ff4e57
TRAC[Mar 31 22:19:09.442] Before database call         dtype=main.FeudDetails
TRAC[Mar 31 22:19:09.446] After database call          dtype=bson.M

【问题讨论】:

  • 我想我找到了一个可能的解决方案(它是 JSON,但它可能对 BSON 有效)。我会将它改编为我的代码,如果它有效,我会将其发布为答案:stackoverflow.com/questions/53453782/…

标签: go bson mgo


【解决方案1】:

在(取消)编组期间忽略 Details

更改Game 的定义,使bson 不会尝试对Details 字段做任何事情:

type Game struct {
    ...
    Details interface{} `json:"details" bson:"-"`
}

手动解组Details

func (game *Game) SetBSON(r bson.Raw) error {
    // Unmarshall everything except Details
    type tempGame Game
    err := r.Unmarshal((*tempGame)(game))  // this is necessary to prevent recursion
    if err != nil {
        return err
    }
    // Get the raw data for Details
    var d struct {
        Details bson.Raw
    }
    if err := r.Unmarshal(&d); err != nil {
        return err
    }
    gameType, ok := games[game.Type]
    if ok {
        // Use individual processing based on game Type
        game.Details, err = gameType.LoadDetails(d.Details)
        if err != nil {
            return err
        }
        // fmt.Sprintf("%T", game.Details) => main.FeudDetails
    }
    return nil
}

手动编组Details

据我所知,在我们告诉它在原始结构中省略 Details 之后,将 bson 包含 Details 的唯一方法是创建一个完整的其他结构,将数据一一复制,并在GetBSON 中使用该类型。看来一定有更好的办法。

【讨论】:

    猜你喜欢
    • 2018-08-01
    • 1970-01-01
    • 2013-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多