我使用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注册他们的类型。但我不喜欢这样会如何向呼叫者展示我的序列化选择。
是否有任何前进路线可以避免这种情况?
答案 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。注册自定义类型时(通过传递它们的值),您将明确引用它们,从而确保它们不会被排除在二进制文件之外。