我正在尝试长期序列化一系列与java中强类层次结构相关的对象,并且由于其简单性,性能和易于升级,我想使用协议缓冲区来实现它。但是,它们并没有为多态性提供太多支持。现在,我处理它的方式是通过“一条消息来统治它们”的解决方案,它有一个必需的字符串uri字段,允许我通过反射实例化正确的类型,然后是一堆可选的字段,用于所有我可以序列化的其他可能的类,只使用其中一个(基于uri字段的值)。是否有更好的方法来处理多态,或者这是否会像我将要获得的那样好?
答案 0 :(得分:34)
在 proto3 中,extend
关键字已被替换。
来自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;
}
但要注意:Any
本质上是一个字节blob。大多数情况下,最好使用Oneof
:
syntax = "proto3";
message A {
string a = 1;
}
message B {
string b = 1;
}
message Foo {
oneof bar {
A a = 1;
B b = 2;
}
}
答案 1 :(得分:33)
有一些实现多态的技术。我试着在这里覆盖它们:Protocol Buffer Polymorphism
我首选的方法是使用嵌套的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;
}
答案 2 :(得分:15)
这不是原始问题的答案,但对于使用Protocol Buffers的v3的其他人可能会有所帮助。版本3不允许使用extensions
关键字。在以下文件上运行protoc
会生成错误消息Extension ranges are not allowed in proto3
。
syntax = "proto3";
message BaseMessage {
extensions 100 to max;
}
答案 3 :(得分:5)
Jon的解决方案是正确的,但却非常奇怪(对我而言)。但是协议缓冲区很简单,所以你可以这样做:
enum Type {
FOO = 0;
BAR = 1;
}
message Foo {
required Type type = 1;
}
message Bar {
required Type type = 1;
required string text = 2;
}
消息栏基本上扩展了消息Foo(当然是从实际的角度来看)。 Java中的实现也很简单:
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());
}
我知道,这不是一个优雅的解决方案,但它简单而合乎逻辑。
答案 4 :(得分:2)
查看Extensions and Nested Extensions以获得更简洁的方法。
答案 5 :(得分:0)
您是否考虑过使用extensions?您可以让您的uri字段确定要使用的类型,然后只加载相应的扩展名。如果您知道您的字段是互斥的,那么您可以在不同的扩展名之间重复使用字段ID。
你必须自己处理这个问题,因为协议缓冲区并不是设计为超出简单值列表的自描述。谷歌技术页面中提到了这一点。
答案 6 :(得分:0)
对我来说,一个更好的解决方案,@ŁukaszMarciniak的回答。
如果Bar扩展了Foo,只需写下:
message Bar {
optional Foo foo = 1;
optional double aDouble = 2;
}
message Foo {
optional string aString = 1;
}
因此,如果Foo进化,则仅修改Foo消息。