当前位置: 首页 > 面试题库 >

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

唐博文
2023-03-14
问题内容

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

这里的难题是,进行灌装的图书馆应忽略所使用的混凝土类型。我希望即使调用者定义了自己的接口实现,也可以进行序列化。

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

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注册其类型。但是我不喜欢这样向调用者显示我的序列化选择。

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


问题答案:

哲学论证

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

通过调用gob.Register()(或gob.RegisterName())您可以清楚地表明
意图 ,您可以使gob包装使用绿灯。这还可以确保该类型 确实 存在,否则在注册时您将无法传递其值。

技术要求

还有一种技术观点规定了此要求(必须事先注册):您无法获得reflect.Type由其string名称给出的类型的类型描述符。不仅您自己,encoding/gob包装也不能做到。

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

您不能按名称“查找”类型的原因是,除非您明确引用它们,否则它们可能不会出现在二进制文件中(它们可能会被“优化”)。注册自定义类型时(通过传递它们的值),您将对它们进行显式引用,从而确保它们不会从二进制文件中排除。



 类似资料:
  • 我想将带入原型。事先不知道它是什么类型的消息。 为了添加更多细节,我知道编码类型/消息可以是的类型集。(它们都是在我自己的proto文件中声明并内置到Go二进制文件中的类型。) 我想看看是否有可能获取一个字节数组并从中重建原始消息。 我写了这个演示:https://play.golang.org/p/WF9KpTlZnp7如果我传递描述符,我可以将其解码为Dynamicpb,并从任何返回消息。

  • 本文向大家介绍MySQL在不知道列名情况下的注入详解,包括了MySQL在不知道列名情况下的注入详解的使用技巧和注意事项,需要的朋友参考一下 前言 最近感觉脑子空空,全在为了刷洞去挖洞,还是回归技术的本身让自己舒服些。好了,下面话不多说了,来一起看看详细的介绍吧 前提 以下情况适用于 MySQL < 5版本,或者在 MySQL >= 5 的版本[存在information_schema库],且已获取

  • 我使用键值存储作为我的Go语言应用程序的后端,日期作为键(保持条目排序),json文档作为值。json的顶级命名空间()以及和存在于我存储的每个json文档中,但在其他方面存在一些差异(尤其是关于一些嵌套的json数据),所以当keyI从数据库中提取时,我真的不知道我在循环浏览的任何时间提取了什么。这是json数据的示例 当我从数据库中提取数据时,我要做的第一件事是将每个条目解组到<code>ma

  • 如果我不知道一个物体的深度,我如何移除另一个物体内部的一个物体? json对象 假设我想删除“4”,但是“4”可能是第一级孩子或者在另一个孩子里面?

  • 问题内容: 我试图解析Java中的JSON字符串并找到键值对,以便我可以确定JSON对象的近似结构,因为JSON字符串的对象结构是未知的。 例如,一个执行可能具有这样的JSON字符串: 另一个像这样: 如何遍历各种JSON元素并确定键及其值?我看着的。我知道如何获取下一个“令牌”并确定令牌的类型(即字段名称,值,数组开头等),但是,我不知道如何获取实际令牌的值。 例如: 或其他库(或)中是否有一个

  • 我知道协议缓冲区是一种序列化格式,需要中的消息格式。proto,以便正确读回。但我有一个文件,我不知道正确的消息格式,因为它没有发布。我想做的是自己对数据进行反向工程,这样我就可以重建消息。为此,我需要读取原始文件,从中可以提取字段编号、类型和值。 有没有一个程序可以做到这一点(最好是在python中,但C/C也很酷)?