在不知道具体类型的情况下解码gob输出

时间:2017-11-02 09:05:12

标签: go gob

我使用gob将结构序列化到磁盘。有问题的结构包含一个接口字段,因此需要使用gob.Register(...)注册具体类型。

这里的皱纹是,进行捕鱼的图书馆应该不了解使用的具体类型。即使调用者已经定义了他们自己的接口实现,我也希望序列化成为可能。

我可以通过动态注册类型来成功编码数据(参见下面的简单示例),但在尝试重新读取该数据时,gob拒绝接受未注册的类型。令人沮丧的是,因为它感觉就像所有的数据一样 - 为什么它不会像main.UpperCaseTransformation结构那样解压缩它作为package main import ( "encoding/gob" "fmt" "os" "strings" ) type Transformation interface { Transform(s string) string } type TextTransformation struct { BaseString string Transformation Transformation } type UpperCaseTransformation struct{} func (UpperCaseTransformation) Transform(s string) string { return strings.ToUpper(s) } func panicOnError(err error) { if err != nil { panic(err) } } // Execute this twice to see the problem (it will tidy up files) func main() { file := os.TempDir() + "/so-example" if _, err := os.Stat(file); os.IsNotExist(err) { tt := TextTransformation{"Hello, World!", UpperCaseTransformation{}} // Note: didn't need to refer to concrete type explicitly gob.Register(tt.Transformation) f, err := os.Create(file) panicOnError(err) defer f.Close() enc := gob.NewEncoder(f) err = enc.Encode(tt) panicOnError(err) fmt.Println("Run complete, run again for error.") } else { f, err := os.Open(file) panicOnError(err) defer os.Remove(f.Name()) defer f.Close() var newTT TextTransformation dec := gob.NewDecoder(f) // Errors with: `gob: name not registered for interface: "main.UpperCaseTransformation"' err = dec.Decode(&newTT) panicOnError(err) } } 结构?

SELECT
    [Document No]
    Distribution
FROM your_table
GROUP BY [Document No], Distribution
HAVING COUNT(*) > 1

我的解决方法是要求界面的实施者用gob注册他们的类型。但我不喜欢这样会如何向呼叫者展示我的序列化选择。

是否有任何前进路线可以避免这种情况?

1 个答案:

答案 0 :(得分:3)

哲学论证

encoding/gob包不能(或者不应该)自己做出决定。由于gob包创建了一个独立于/从应用程序分离的序列化形式,因此无法保证解码器中存在接口类型的值;即使它们(具体类型名称匹配),也不能保证它们代表相同的类型(或给定类型的相同实现)。

通过调用gob.Register()(或gob.RegisterName())来清除 intent ,您可以向gob包提供绿灯以使用该类型。这也确保类型存在,否则在注册时你将无法传递它的值。

技术要求

还有一个技术观点规定了这个要求(你必须事先注册):你不能获得string名称给出的reflect.Type类型描述符。不只是你,encoding/gob包也不能这样做。

因此,要求您先前调用gob.Register()gob包将收到相关类型的值,因此它可以(并且它将)访问并存储其reflect.Type内部描述符,所以当检测到这种类型的值时,它能够创建这种类型的新值(例如使用reflect.New()),以便将正在解码的值存储到其中。

您不能按名称“查找”类型的原因是它们可能不会以二进制文件结尾(它们可能会被“优化”),除非您明确地引用它们。有关详细信息,请参阅Call all functions with special prefix or suffix in Golang;和Splitting client/server code。注册自定义类型时(通过传递它们的值),您将明确引用它们,从而确保它们不会被排除在二进制文件之外。