De- и encode interface {} с Gob

Я пытаюсь де- и кодировать структуру, содержащую поле Interface {} as.

Проблема в том, что кодировка работает нормально, но если я попытаюсь декодировать данные до data, значение получит { <nil>}.

Это действительно работает, если я изменяю Data interface{} на Data substring, но это не решение для меня, потому что я хочу кэшировать результаты запроса в базу данных, которые имеют разные типы в зависимости от запроса. (например, Users или Cookies)

Минимальный рабочий пример

Источник

http://play.golang.org/p/aX7MIfqrWl

package main

import (
    "bytes"
    "encoding/gob"
    "fmt"
)

type Data struct {
    Name string
    Data interface{}
}

type SubType struct {
    Foo string
}

func main() {
    // Encode
    encodeData := Data{
        Name: "FooBar",
        Data: SubType{Foo: "Test"},
    }
    mCache := new(bytes.Buffer)
    encCache := gob.NewEncoder(mCache)
    encCache.Encode(encodeData)

    fmt.Printf("Encoded: ")
    fmt.Println(mCache.Bytes())

    // Decode
    var data Data
    pCache := bytes.NewBuffer(mCache.Bytes())
    decCache := gob.NewDecoder(pCache)
    decCache.Decode(&data)

    fmt.Printf("Decoded: ")
    fmt.Println(data)
}

Результаты

Ожидаемый результат

Закодировано: [37 255 129 3 1 1 4 68 97 116 97 1 255 130 0 1 2 1 4 78 97 109 101 1 12 0 1 4 68 97 116 97 1 255 132 0 0 0 29 255 131 3 1 1 7 83 117 98 84 121 112 101 1 255 132 0 1 1 1 3 70 111 111 1 12 0 0 0 19 255 130 1 6 70 111 111 66 97 114 1 1 4 84 101 115 116 0 0]

Декодировано: {FooBar {Test}}

Текущий результат

Закодировано: [37 255 129 3 1 1 4 68 97 116 97 1 255 130 0 1 2 1 4 78 97 109 101 1 12 0 1 4 68 97 116 97 1 255 132 0 0 0 29 255 131 3 1 1 7 83 117 98 84 121 112 101 1 255 132 0 1 1 1 3 70 111 111 1 12 0 0 0 19 255 130 1 6 70 111 111 66 97 114 1 1 4 84 101 115 116 0 0]

Декодировано: {}

Ответ 1

Вы не можете декодировать в интерфейс, потому что декодер не имеет возможности определить, какой тип должно быть полем.

Вы можете справиться с этим несколькими способами. Один из них заключается в том, чтобы Data хранит структуру с полем для каждого типа, который может быть декодирован. Но тип может быть очень сложным.

Другой способ - реализовать интерфейс GobDecoder и GobEncoder для вашей структуры и реализовать собственную сериализацию для типов. Это, вероятно, не идеально, хотя.

Возможно, лучший подход состоит в том, чтобы вместо этого использовать кеш-память определенных типов и использовать отдельный метод для каждого типа. Использовать ваш пример. У вашего приложения будет кеш-метод с именем GetSubType(key string) (*SubType, error) в кеше. Это приведет к возврату конкретного типа или ошибки декодирования вместо интерфейса. Это было бы более чистым и читаемым, а также более типичным.

Ответ 2

Проблема заключается в том, что в вашем коде есть ошибка при выполнении encCache.Encode(encodeData), но поскольку вы не проверяете наличие ошибки, вы этого не понимаете. Выход пуст, потому что encodedData не удается правильно закодировать.

Если вы добавите проверку ошибок,

err := enc.Encode(encodeData)
if err != nil {
    log.Fatal("encode error:", err)
}

Затем вы увидите что-то вроде

2013/03/09 17:57:23 encode error:gob: type not registered for interface: main.SubType

Если вы добавите одну строку в исходный код до enc.Encode(encodeData),

gob.Register(SubType{})

Затем вы получите ожидаемый результат.

Decoded: {FooBar {Test}}

См. http://play.golang.org/p/xt4zNyPZ2W