I am wondering if there is a generic way to deserialize a message that has a oneof
sub-message. The oneof
message will contain many other messages. Right now, I can get it working using a switch
statement on the case
number and call the appropriate getter. But, if there is a generic way so that in the future I don't have to keep adding new cases.
I would like to eliminate the cases and have something generic where I could get the message type ie Exit
and call the correct getter. Is there a way to do it with reflection? I looked for examples online but couldn't find much.
proto file:
message AllClientMessages {
oneof client_message {
util.client.command Exit exit = 1
util.client.command SendNew sendnew = 2
util.client.command CancelAll cancelAll = 3
// ... (more than 60 messages)
}
}
Code ( buf
contains the serialized message):
const std::unique_ptr<google::protobuf::Message> ProtoBuilder::deserialize(char* buf)
{
AllClientMessages clientMsg;
clientMsg.ParseFromString(buf);
switch(clientMsg.client_message_case())
{
case AllClientMessages::kExitFieldNumber: //generate enums
return make_unique<Exit>(clientMsg.exit());
case AllClientMessages::kSendNewFieldNumber:
return make_unique<SendNew>(clientMsg.sendnew());
// I access the getter and return unique_ptr to base protobuf Message.
// ... I do this for each case.
}
}
Here's some code that should work for this.
Some time at initialization you can construct a map between one-of field number and index.
auto descriptor = clientMsg.GetDescriptor();
auto oneofDescriptor = descriptor->FindOneofByName("client_message");
// Map from oneof field number to field index
std::unordered_map<int, int> numberToIndexMap;
for (int idx = 0; idx < oneofDescriptor->field_count(); idx++)
{
auto fieldDescriptor = oneofDescriptor->field(idx);
numberToIndexMap[fieldDescriptor->number()] = idx;
}
Then later on you can use that map to access the correct field indicated by the type_case .
auto reflection = clientMsg.GetReflection();
auto descriptor = clientMsg.GetDescriptor();
auto oneofDescriptor = descriptor->FindOneofByName("client_message");
const auto & message = reflection->GetMessage(clientMsg, oneofDescriptor->field(numberToIndexMap.at(int(event.type_case()))));
// If we want a new copy to return as unique ptr
auto copy = std::make_unique<google::protobuf::Message>(message.New());
copy->CopyFrom(message);
return copy;
I haven't been able to find a way to get the one-of descriptor without hardcoding the name of the one-of, which feels kind of lame. It would be nice to be about to get it directly from AllClientMessages.client_message().
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.