what's the right way to do polymorphism with protocol buffers?
Solution 1:
In proto3 the extend
keyword has been replaced.
From the docs: If you are already familiar with proto2 syntax, the Any type replaces extensions.
syntax = "proto3";
import "google/protobuf/any.proto";
message Foo {
google.protobuf.Any bar = 1;
}
But beware: Any
is essentially a bytes blob. Most of the times it is better to use Oneof
:
syntax = "proto3";
message A {
string a = 1;
}
message B {
string b = 1;
}
message Foo {
oneof bar {
A a = 1;
B b = 2;
}
}
Solution 2:
There are a few techniques for implementing polymorphism. I try to cover them all here: Protocol Buffer Polymorphism
My preferred approach uses nested extensions:
message Animal
{
extensions 100 to max;
enum Type
{
Cat = 1;
Dog = 2;
}
required Type type = 1;
}
message Cat
{
extend Animal
{
required Cat animal = 100; // Unique Animal extension number
}
// These fields can use the full number range.
optional bool declawed = 1;
}
message Dog
{
extend Animal
{
required Dog animal = 101; // Unique Animal extension number
}
// These fields can use the full number range.
optional uint32 bones_buried = 1;
}
Solution 3:
Jon's solution is correct and working but pretty weird (for me). But Protocol Buffers is quite simple, so You can do something like that:
enum Type {
FOO = 0;
BAR = 1;
}
message Foo {
required Type type = 1;
}
message Bar {
required Type type = 1;
required string text = 2;
}
Basically message Bar extends message Foo (from practical side of course). Implementation in Java is simple too:
Bar bar = Bar.newBuilder().setType(Type.BAR).setText("example").build();
byte[] data = bar.toByteArray();
----
Foo foo = Foo.parseFrom(data);
if(foo.getType() == Type.BAR){
Bar bar = Bar.parseFrom(data);
System.out.println(bar.getText());
}
I known, it's not an elegant solution, but it's simple and logical.
Solution 4:
Check out Extensions and Nested Extensions for a slightly cleaner way to do this.