用协议缓冲区做多态的正确方法是什么?

问题描述 投票:48回答:7

我正在尝试长期序列化一堆与java中强类层次结构相关的对象,并且我想使用协议缓冲区来实现它,因为它们的简单性,性能和易于升级。但是,它们并没有为多态性提供太多支持。现在,我处理它的方式是通过“一条消息来统治它们”的解决方案,它有一个必需的字符串uri字段,允许我通过反射实例化正确的类型,然后是一堆可选的字段,用于所有我可以序列化的其他可能的类,只使用其中一个(基于uri字段的值)。有没有更好的方法来处理多态,或者这是否会像我将得到的那样好?

protocol-buffers
7个回答
34
投票

在proto3中,extend关键字已被替换。来自docsIf 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;
  }
}

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;
}

15
投票

这不是原始问题的答案,但它可能对使用Protocol Buffers v3的其他人有帮助。版本3不允许使用extensions关键字。在以下文件上运行protoc会生成错误消息Extension ranges are not allowed in proto3

syntax = "proto3";

message BaseMessage {
  extensions 100 to max;
}

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());
}

我知道,这不是一个优雅的解决方案,但它简单而合乎逻辑。


2
投票

查看Extensions and Nested Extensions以获得更清晰的方式。


0
投票

你考虑过使用extensions吗?您可以让您的uri字段确定要使用的类型,然后只加载相应的扩展名。如果您知道您的字段是互斥的,那么您可以在不同的扩展名之间重复使用字段ID。

您必须自己处理这个问题,因为协议缓冲区不是设计为超出简单值列表的自描述。谷歌技术页面中提到了这一点。


0
投票

@ŁukaszMarciniak的答案对我来说是一个更好的解决方案。

如果Bar扩展了Foo,只需写:

message Bar {
   optional Foo foo = 1;
   optional double aDouble = 2;
}
message Foo {
   optional string aString = 1;
}

因此,如果Foo进化,只有Foo消息被修改。

© www.soinside.com 2019 - 2024. All rights reserved.