Reputation: 4879
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?
Upvotes: 1
Views: 1197
Reputation: 4879
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
}
Upvotes: 1