动态创建协议缓冲区对象

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

Dynamically create protocol buffer object

问题

我正在使用一组协议缓冲区(protocol buffers)进行工作,我对如何实例化它们并在运行时计算有些困惑。我知道我需要使用reflect来做到这一点,但似乎没有什么方法可行。在编译时,我只知道我需要创建的第一个协议缓冲区。该缓冲区包含一个字段,告诉我下一个需要创建的缓冲区的整数ID。

我所有的缓冲区都在一个名为kpb的包中定义。

我通过以下方式找到第一个缓冲区:

info := &kpb.ArchiveInfo{}

err := proto.Unmarshal(<byte array>, info)

// 错误处理

messageType = *info.MessageType // 1

我有一个定义了下一个需要调用的缓冲区的映射表。该映射表定义如下:

请注意,该映射表的所有值都是proto.ProtoMessage对象,但使用它似乎引发了比解决的更多问题。

var registryMap = map[uint32]interface{}{
  1: &kpb.KNDocumentArchive{},
  ...等等
}

因此,当我引用这个映射表时,我这样做:

var klass interface{}

// 其他操作

klass = registryMap[messageType]
fmt.Println(klass) // *kpb.KNDocumentArchive

但是,我无法弄清楚如何使用正确的类型实例化一个变量,以便对我拥有的有效负载进行解组。

我可以通过以下方式获取klasstype

klassType := reflect.TypeOf(klass)
fmt.Println(klassType) // kpb.KNDocumentArchive - 如预期所示

但是,如果我尝试使用它创建一个新变量,就会出现错误:

payloadObj := new(klass)
// klassType(reflect.Type类型的变量)不是一个类型

因此,即使类型是kpb.KNDocumentArchive,就像我期望的那样,它仍然是reflect.Type类型。

当我将proto.ProtoMessage用作映射表返回的类型时,我可以绕过这一部分并实例化变量,但我无法将其传递给proto.Unmarshal,因为它正确地期望类型为kpb.KNDocumentArchive

我在这里做错了什么?

英文:

I'm working with a set of protocol buffers and I'm having trouble wrapping my head around what I need to do to instantiate them and it has to be calculated at runtime. I know I somehow need to use reflect to do this, but nothing seems to work. At compile time, I only know the first protocol buffer that I need to create. That buffer contains a field that tells me an integer id of the next one I need to create.

All of my buffers are defined in a package called kpb.

I find that first one like this:

info := &amp;kpb.ArchiveInfo{}

err := proto.Unmarshal(&lt;byte array&gt;, info)

// error handling

messageType = *info.MessageType // 1

I have a map that defines the next buffers I need to call. That map is defined like this:

Note that all of the values of this map are proto.ProtoMessage objects, but using that seemed to cause more problems than it solved.

var registryMap = map[uint32]interface{}{
  1: &amp;kpb.KNDocumentArchive{},
  ...etc
}

So when I reference this map, I'm doing it like this:

var klass interface{}

//stuff

klass = registryMap[messageType]
fmt.Println(klass) // *kpb.KNDocumentArchive

But, what I can't figure out is how to instantiate a variable with the proper type to Unmarshal the payload I have.

I can get the type of the klass by doing this:

klassType := reflect.TypeOf(klass)
fmt.Println(klassType) // kpb.KNDocumentArchive - as expected

But, if I try to create a new variable with it, I get an error

payloadObj := new(klass)
// klassType (variable of type reflect.Type) is not a type

So even though the type is kpb.KNDocumentArchive like I expect, it's still somehow reflect.Type

When I used proto.ProtoMessage as the type for the map return, I could get past this part and have the variable instantiated, but I couldn't pass that to proto.Unmarshal because it, rightly, expects the type to be kpb.KNDocumentArchive

What am I doing wrong here?

答案1

得分: 1

我已经弄清楚了。我需要使用内置的 proto 功能。最终的结果是将我的映射更新为:

var registryMap = map[uint32]string{
  1: "KN.KNDocumentArchive",
}

然后,使用以下代码解决了反序列化部分:

func GetProto(id uint32, messageBytes []byte) proto.Message {
	klass := registryMap[id]

	if klass == "" {
		panic("不知道如何解析 Protobuf 消息类型 " + fmt.Sprint(id))
	}

	messageName := protoreflect.FullName(klass)

	pbtype, err := protoregistry.GlobalTypes.FindMessageByName(messageName)

	if err != nil {
		panic(err)
	}

	msg := pbtype.New().Interface()
	err = proto.Unmarshal(messageBytes, msg)

	if err != nil {
		panic(err)
	}

	return msg
}
英文:

I was able to figure this out. I needed to use proto built in features. The end result was updating my map to this:

var registryMap = map[uint32]string{
  1: &quot;KN.KNDocumentArchive&quot;,
}

And then the unmarshaling part was solved with this:

func GetProto(id uint32, messageBytes []byte) proto.Message {
	klass := registryMap[id]

	if klass == &quot;&quot; {
		panic(&quot;Don&#39;t know how to parse Protobuf message type &quot; + fmt.Sprint(id))
	}

	messageName := protoreflect.FullName(klass)

	pbtype, err := protoregistry.GlobalTypes.FindMessageByName(messageName)

	if err != nil {
		panic(err)
	}

	msg := pbtype.New().Interface()
	err = proto.Unmarshal(messageBytes, msg)

	if err != nil {
		panic(err)
	}

	return msg
}

huangapple
  • 本文由 发表于 2022年1月28日 23:48:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/70896430.html
匿名

发表评论

匿名网友

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

确定