Accessing undelying Go type using `protoreflect`
topofstack opened this issue · comments
I'm pretty new to protobuf reflection and the question may sound pretty dumb. Is there any other way I can get access to underlying Go type using protoreflect
?
Here's small proto which includes another widely used proto:
import "google/protobuf/timestamp.proto";
message MyMessage {
string name = 1;
google.protobuf.Timestamp tstamp = 2;
}
Here's the code I use to parse incoming byte array on the basis of the model generated by proto-compiler
// instantiate a message generated by protoc
m := desc.MyMessage{
Name: "AwesomeName",
Tstamp: timestamppb.New(time.Now()),
}
bytes := Marshal(m) // serialize message into byte array
// let the package know about the stucture of the MyMessage
msg := dynamicpb.NewMessage(m.ProtoReflect().Descriptor())
err := proto.Unmarshal(bytes, msg)
if err != nil {
return nil, err
}
msg.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
if fd.Kind() == protoreflect.MessageKind {
if fd.Message().FullName() == "google.protobuf.Timestamp" { // have to find it by unique name :(
timestampMsg := msg.Get(fd).Message()
secondsDesc := timestampMsg.Descriptor().Fields().ByName("seconds")
secondsVal := timestampMsg.Get(secondsDesc).Int()
nanosDesc := timestampMsg.Descriptor().Fields().ByName("nanos")
nanosVal := timestampMsg.Get(nanosDesc).Int()
goTime := time.Unix(secondsVal, nanosVal) // <<-- finally got Go Time object
}
}
return true
})
Is there any smarter way to implement it?
I’m not sure why you would want to use dynamicpb
when you have the actual message type?
var msg pb.MyMessage
if err := proto.Unmarshal(bytes, &msg) {
return nil, err
}
goTime := msg.Tstamp.AsTime() // get the Go time.Time object
Thanks for response!
I'm trying to write some kind of an abstract proto parser which should extract fields from different protos/messages
It should work like:
-
User passes MessageDescriptor to the Parser. It acts as a template for incoming binary messages. Now I can inspect internal representation of the incoming byte arrays using
protoreflect
. -
User calls something like
Parser.parse(byte[])
and in this method passed byte array will be dynamically inspected without knowing it's actual underlying message type. All I know is that this byte array corresponds with theMessageDescriptor
i got on the step 1. I need to extract all fields and cast them into Go types for later usage.
The conversion from google.protobuf.Timestamp
to time.Time
from the code above doesn't look good. So, I'm wondering, if there's a more clear way to perform it.
I’m unsure, but a type switch might enable you to go from .Message()
to the timestamppb
and then you should be able to .AsTime()
at that point…
Sadly, all out-of-the-box conversions are defined in https://github.com/protocolbuffers/protobuf-go/blob/v1.30.0/reflect/protoreflect/value_union.go.
So, it looks like the only way is to treat timestamp as a message and parse it by hand like I initially did.