用协议缓冲区做多态的正确方法是什么?
我试图长期序列化一堆强大的类相关的对象在java中,我想使用协议缓冲区来做到这一点,由于其简单性,性能和易于升级。 但是,他们不提供多态的支持。 现在,我处理它的方式是有一个“一条消息来统治所有”的解决scheme,它有一个必需的stringuri字段,允许我通过reflection来实例化正确的types,然后一堆可选字段其他可能的类我可以序列化,只有其中一个将被使用(基于uri字段的值)。 有没有更好的方法来处理多态性,还是这样好,我会得到?
有几种实现多态的技术。 我试图在这里涵盖它们: 协议缓冲区多态性
我的首选方法使用嵌套扩展 :
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; }
在proto3中 , extend
关键字已被replace。 从文档 : If you are already familiar with proto2 syntax, the Any type replaces extensions.
syntax = "proto3"; import "google/protobuf/any.proto"; message Foo { Any bar = 1; }
但要小心: Any
实质上是一个字节块。 大多数情况下最好使用Oneof
:
syntax = "proto3"; message A { string a = 1; } message B { string b = 1; } message Foo { oneof bar { A a = 1; B b = 2; } }
这不是原来的问题,但对使用协议缓冲区的 v3的其他人可能是有帮助的。 版本3不允许使用extensions
关键字。 在以下文件上运行protoc
生成一个错误消息Extension ranges are not allowed in proto3
。
syntax = "proto3"; message BaseMessage { extensions 100 to max; }
乔恩的解决scheme是正确的,工作,但很奇怪(对我来说)。 但Protocol Buffers很简单,所以你可以这样做:
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()); }
我知道,这不是一个优雅的解决scheme,但它简单而合乎逻辑。
检查扩展和嵌套扩展一个稍微干净的方式来做到这一点。
你有没有考虑过使用扩展 ? 你可以让你的uri字段决定使用的types,然后加载相应的扩展。 如果您知道您的字段是互斥的,那么您可以在不同的扩展名之间重复使用字段ID。
你必须自己处理这一切,因为协议缓冲区并不是被devise成超越简单的值列表进行自我描述的。 这是在谷歌技术网页上触及的。
一个更好的解决scheme,对我来说,@ŁukaszMarciniak的答案。
如果Bar延伸Foo,简单地写:
message Bar { optional Foo foo = 1; optional double aDouble = 2; } message Foo { optional string aString = 1; }
所以如果Foo只演变Foo消息被修改。