Roberto
Roberto

Reputation: 11933

How can I tell what kind of message is in a serialized protocol-buffer?

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

Answers (1)

Marc Gravell
Marc Gravell

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

Related Questions