Как мне использовать bson.SetBSON или bson.Raw? - PullRequest
0 голосов
/ 01 апреля 2020

Возможно связано: Как использовать тип интерфейса в качестве модели в м go (Go)?

У меня есть такая структура:

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

type FeudDetails struct {
    ...
}

type TriviaDetails struct {
    ...
}

type BingoDetails struct {
    ...
}

Я хочу использовать Type поле Game для десериализации Details в специфицированный c тип (например, экземпляр FeudDetails или BingoDetails). Это все равно будет interface{} в Game, но тогда я смогу сделать это:

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

В документах говорится, что "возможно частично или маршалировать значения" используя 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
}

Я получаю (хм ) переполнение стека здесь. Я предполагаю, что это потому, что 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 ...»

РЕДАКТИРОВАТЬ: Я также попытался десериализовать нормально, а затем произвел приведение interface{} версия Details с использованием game.Details.(FeudDetails), но преобразование не удалось. Я думаю, я не могу сделать это таким образом, потому что базовый тип после десериализации - не a FeudDetails, а скорее map[string]interface{}.

РЕДАКТИРОВАТЬ 2: Я думал, что буду умным и предварительно заполню объект нужными типами при извлечении из базы данных (game := Game{Details: FeudDetails: {}} до вызова 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

1 Ответ

0 голосов
/ 03 апреля 2020

Игнорировать Details во время (не) сортировки

Изменить определение Game, чтобы bson не пытался что-либо сделать с полем Details:

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

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

Marshal Details вручную

Насколько я могу судить, единственный способ получить bson до включает Details после того, как мы сказали, что нужно опустить его в исходной структуре, это создать совершенно другую структуру, скопировать данные по одному и использовать этот тип в GetBSON. Кажется, должен быть лучший путь.

...