Декодировать и кодировать интерфейс {} с помощью Gob

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

Проблема в том, что кодировка работает нормально, но если я попытаюсь декодировать данные до 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]

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


person Testuser    schedule 02.01.2013    source источник
comment
Как декодер должен определять тип для декодирования?   -  person Denys Séguret    schedule 02.01.2013
comment
var data Data и decCache.Decode(&data) должны справиться с этим, или мне что-то здесь не хватает?   -  person Testuser    schedule 02.01.2013
comment
Это дает адрес для декодирования, но не тип, если Data является интерфейсом {}.   -  person Denys Séguret    schedule 02.01.2013


Ответы (2)


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

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

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

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

person Jeremy Wall    schedule 02.01.2013
comment
Третий подход (GetSubType) мне кажется абсолютно разумным. Спасибо! - person Testuser; 03.01.2013
comment
Голосование против, потому что это неправильный ответ; Ответ Дмитрия показывает очень простой способ заставить его работать. - person Bryan; 10.02.2015

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

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

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.

person Dmitri Shuralyov    schedule 09.03.2013