在不知道具体类型的情况下解码gob输出 [英] Decode gob output without knowing concrete types

查看:126
本文介绍了在不知道具体类型的情况下解码gob输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using gob to serialize structs to disk. The struct in question contains an interface field, so the concrete type needs to be registered using gob.Register(...).

这里的难题是,进行gob-ing的库应该对所使用的具体类型一无所知.我希望即使调用者定义了自己的接口实现,也可以进行序列化.

The wrinkle here is that the library doing the gob-ing should be ignorant of the concrete type in use. I wanted the serialization to be possible even when callers have defined their own implementations of the interface.

我可以通过动态注册类型来成功编码数据(请参见下面的简单示例),但是在尝试重新读取数据时,gob拒绝接受未注册的类型.令人沮丧的是,因为感觉好像所有数据都在那里-如果gob标记为这样,为什么不将gob仅仅解压缩为main.UpperCaseTransformation结构?

I can successfully encode the data by registering the type on the fly (see trivial example below), but upon trying to re-read that data, gob refuses to accept the un-registered type. Its frustrating, because it feels like all the data is there - why isn't gob just unpacking that as a main.UpperCaseTransformation struct if it's labelled as such?

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)
    }
}

我的解决方法是要求接口的实现者向gob注册其类型.但是我不喜欢这样向调用者显示我的序列化选择.

My work-around would be to require implementers of the interface to register their type with gob. But I don't like how that reveals my serialization choices to the callers.

有没有避免这种情况的前进路线?

Is there any route forward that avoids this?

推荐答案

哲学论证

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

Philosophical argumentation

The encoding/gob package cannot (or rather should not) make that decision on its own. Since the gob package creates a serialized form independent of / detached from the app, there is no guarantee that values of interface types will exist in the decoder; and even if they do (matched by the concrete type name), there is no guarantee that they represent the same type (or the same implementation of a given type).

通过调用 gob.Register() (或

By calling gob.Register() (or gob.RegisterName()) you make that intent clear, you give green light to the gob package to use that type. This also ensures that the type does exist, else you would not be able to pass a value of it when registering.

还有一种技术观点规定了此要求(必须事先注册):您无法获得

There's also a technical point of view that dictates this requirement (that you must register prior): you cannot obtain the reflect.Type type descriptor of a type given by its string name. Not just you, the encoding/gob package can't do it either.

因此,通过要求您先调用gob.Register()gob程序包将收到一个有问题的类型的值,因此它可以(并且将)在内部访问和存储其reflect.Type描述符,因此当检测到此类型的值时,它便能够创建此类型的新值(例如,使用 reflect.New() ),以便将要解码的值存储到其中.

So by requiring you to call gob.Register() prior, the gob package will receive a value of the type in question, and therefore it can (and it will) access and store its reflect.Type descriptor internally, and so when a value of this type is detected, it is capable of creating a new value of this type (e.g. using reflect.New()) in order to store the value being decoded into it.

您不能按名称查找"类型的原因是,除非您明确引用它们,否则它们可能不会出现在二进制文件中(它们可能会被优化").有关详细信息,请参见以特殊方式调用所有函数Golang的前缀或后缀;和拆分客户端/服务器代码.注册自定义类型时(通过传递它们的值),您将对它们进行显式引用,从而确保它们不会从二进制文件中排除.

The reason why you can't "lookup" types by name is that they may not end up in your binary (they may get "optimized out") unless you explicitly refer to them. For details see Call all functions with special prefix or suffix in Golang; and Splitting client/server code. When registering your custom types (by passing values of them), you are making an explicit reference to them and thus ensuring that they won't get excluded from the binaries.

这篇关于在不知道具体类型的情况下解码gob输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆