Reputation: 11933
I thought I would get an InvalidProtocolBufferException
when trying to parseFrom(bytes)
every time I do not use the expected message class.
My code looks like this:
try {
LoginMessage msg = LoginMessage.parseFrom(bytes);
// ... use msg ...
} catch(InvalidProtocolBufferException exception) {
ErrorMessage error = ErrorMessage.parseFrom(bytes);
// ... do something ...
}
And my messages are these:
message LoginMessage
{
required Status status = 1;
optional UserMessage user= 2;
}
message ErrorMessage
{
required Error error = 1;
}
where TeamStatus
and Error
are Enums.
When I execute the code that should result in an ErrorMessage
, it parses to a LoginMessage
and confuses both enums: error
field with status
field.
So how could I differ these two types of message?
It seems that it uses structural typing for optimization, so it doesn't transmit field names or message types, but what would be the practical way to solve this problem? I don't want to insert the ErrorMessage
in the LoginMessage
and always return a LoginMessage
.
I thought about setting the error
field index to a number that no other message will use, like ErrorMessage error = 15000;
, but I don't know if this is right, nor if it will always work (imagine if all fields in LoginMessage
were optional, would it work?).
Upvotes: 1
Views: 277
Reputation: 1062580
No, that won't error. Enums are just integers when serialized, and unexpected values are stored in the extension data - so field 1 is entirely interchangeable between the messages. Since "user" is optional, it won't matter if it isn't there - and if it is there but isn't expected, it will be stored in the extension data.
It seems that it uses structural typing for optimization, so it doesn't transmit field names or message types
That is correct; only field numbers and data are transferred. No names.
The best way to tell messages apart is via some kind of prefix. The easiest way t do this is with a wrapper message:
message SomeWrapper {
optional LoginMessage login = 1;
optional ErrorMessage error = 2;
}
Then when deserializing, just check which field has a value. Obviously when serialising you'll need to wrap the value in a SomeWrapper, too. Note that optional values that aren't included incur zero cost.
Upvotes: 2