views:

34

answers:

2

I'm using Google Protocol Buffers to serialize some of my business objects (in a Java app). As recommended in the tutorials, I wrap the message builder in a class of my own that implements getter and setter methods to access the message's properties. Also, I declared all message fields optional, again following their recommendations.

Now, I can give any of the wrapper classes any of the encoded messages and they will always parse and accept them. This leads to wrapper objects that represent a message type which they don't actually contain and a lot of bogus happens.

When loading the binary content of a message into a wrapper class, how can I make it throw an error if it has been passed the wrong type?

The solution I'm currently thinking of would have all messages extend a base message with a required type field (and maybe a version field). This would have the generated builder class throw an exception if those fields are missing, and if they are there, I can check in my own code. However, I'm not yet done evaluating what repercussions this has for my code, and I'm not sure this is going to be easy.

+1  A: 

If the data you pass to MyMessage.parseFrom() does not represent a message of that type, you will get a InvalidProtocolBufferException. Isn't that enough for you?

PB messages are not self-describing, so need to know (by some means) which message you are trying to parse. Of course, you can try to parse them and catch InvalidProtocolBufferException, but that isn't very nice. Instead, I think most people are using the approach you are describing: use a base message class with a type field (usually an enum) and a number of optional fields, one for each possible sub-type. This allows you to parse the message, and then switch on the message type to extract the actual "payload" of the message.

JesperE
If all fields on all messages are optional, you don't get InvalidProtocolBufferException. At least I didn't, and it took me a while to figure out why my objects were bogus. I expected to get some kind of error if I just try to deserialize the wrong message, but after thinking about it, it now makes sense to me, because I couldn't see how the parser should ever distinguish them.
Hanno Fietz
Extending messages didn't work out (because extending Foo means you get different classes all called Foo, not a child of Foo, i. e. you can't have the different extended classes next to each other). Having a wrapper message with a required type and payload field now works fine.
Hanno Fietz
A: 

This seems to be what other people do, too, and it works fine for me:

message TypedMessage {
    required string type = 1;
    required bytes payload = 2;
}

The actual message goes into the payload field in serialized form and the type is used to get the proper builder and wrapper class. The field could also be an enum, I'm currently using Java class names, which I will likely replace by a different system later, since this means refactoring breaks backwards compatibility of the parser.

Hanno Fietz