使用Gob对interface{}进行解码和编码

huangapple go评论76阅读模式
英文:

De- and encode interface{} with Gob

问题

我正在尝试对包含Interface{}字段的结构进行解码和编码。

问题在于,编码工作正常,但是如果我尝试将数据解码到data,值会变成{ <nil>}

如果我将Data interface{}更改为Data substring,它实际上是有效的,但对我来说这不是一个解决方案,因为我想要缓存对数据库的查询结果,这些结果根据查询的不同具有不同的类型(例如UsersCookies)。

#最小工作示例

##源代码
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() {
    // 编码
    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())

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

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

##输出
###预期输出
Encoded: [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]

Decoded: {FooBar {Test}}
###当前结果
Encoded: [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]

Decoded: { <nil>}

英文:

I'm trying to de- and encode a struct which contains a Interface{} as field.

The problem there is, that the encoding works fine, but if I try to decode the data to data the value gets { &lt;nil&gt;}.

It actually works, if I change Data interface{} to Data substring, but this is not a solution for me because I want to cache the results of a query to a database which have different types depending on the query. (e.g. Users or Cookies)

#Minimal working example

Source

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

package main

import (
	&quot;bytes&quot;
	&quot;encoding/gob&quot;
	&quot;fmt&quot;
)

type Data struct {
	Name string
	Data interface{}
}

type SubType struct {
	Foo string
}

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

	fmt.Printf(&quot;Encoded: &quot;)
	fmt.Println(mCache.Bytes())

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

	fmt.Printf(&quot;Decoded: &quot;)
	fmt.Println(data)
}

##Outputs
###Expected output
Encoded: [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]

Decoded: {FooBar {Test}}
###Current Result
Encoded: [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]

Decoded: { <nil>}

答案1

得分: 44

问题是在你的代码中,当执行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

英文:

The problem is that in your code, there is an error when executing encCache.Encode(encodeData) but since you don't check for error, you don't realize that. The output is blank because encodedData fails to get encoded properly.

If you add error checking,

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

Then you'd see something like

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

If you add one line to your original code before enc.Encode(encodeData),

gob.Register(SubType{})

Then you get expected output.

Decoded: {FooBar {Test}}

See http://play.golang.org/p/xt4zNyPZ2W

答案2

得分: -7

你不能将解码结果转换为接口,因为解码器无法确定字段的类型。

你可以用几种不同的方式来处理这个问题。一种方式是让Data持有一个包含每种可能被解码的类型的字段的结构体。但是这种类型可能非常复杂。

另一种方式是为你的结构体实现GobDecoder和GobEncoder接口,并为这些类型实现自己的序列化。不过这可能不是最理想的方式。

也许最好的方法是让缓存存储特定的类型,并为每种类型使用单独的方法。以你的例子为例,你的应用程序可以在缓存上有一个名为GetSubType(key string) (*SubType, error)的方法。它会返回具体的类型或解码错误,而不是一个接口。这样做会更清晰、更易读,也更类型安全。

英文:

You can't decode into an interface because the decoder has no way to determine what type the field should be.

You can handle this in a few different ways. One is to have Data hold a struct with a field for every type that could be decoded. But the type could be very complicated.

The other way is to implement the GobDecoder and GobEncoder interface for your struct and implement your own serialization for the types. This is probably not ideal though.

Perhaps the best approach is to have the cache store specific types instead and use separate method for each type. To use your example. Your application would have a cache method called GetSubType(key string) (*SubType, error) on the cache. This would return the concrete type or a decoding error instead of an interface. It would be cleaner and more readable as well as more typesafe.

huangapple
  • 本文由 发表于 2013年1月2日 19:41:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/14121422.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定