英文:
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
但是,我无法弄清楚如何使用正确的类型实例化一个变量,以便对我拥有的有效负载进行解组。
我可以通过以下方式获取klass
的type
:
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 := &kpb.ArchiveInfo{}
err := proto.Unmarshal(<byte array>, 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: &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: "KN.KNDocumentArchive",
}
And then the unmarshaling part was solved with this:
func GetProto(id uint32, messageBytes []byte) proto.Message {
klass := registryMap[id]
if klass == "" {
panic("Don't know how to parse Protobuf message type " + 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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论