web-dev-qa-db-ja.com

プロトコルバッファでポリモーフィズムを行う正しい方法は何ですか?

私は、Javaの強力なクラス階層によって関連付けられた一連のオブジェクトを長期的にシリアル化しようとしています。そのシンプルさ、パフォーマンス、アップグレードの容易さから、プロトコルバッファーを使用してそれを実行したいと思います。ただし、ポリモーフィズムのサポートはあまりありません。今、私がそれを処理する方法は、リフレクションを介して正しいタイプをインスタンス化することができる必須の文字列uriフィールドを持つ「すべてを支配する1つのメッセージ」ソリューションを持っていることです。私がシリアル化できる他の可能なクラスで、そのうちの1つだけが使用されます(uriフィールドの値に基づいて)。ポリモーフィズムを処理するより良い方法はありますか?

49
user364003

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

多態性を実装するためのテクニックがいくつかあります。ここでそれらすべてをカバーしようとしています: 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;
}
33
Jon Parise

これは元の質問に対する答えではありませんが、v3の Protocol Buffers を使用している他の人にとっては役に立つかもしれません。バージョン3は extensions キーワードを許可しません。次のファイルでprotocを実行すると、メッセージExtension ranges are not allowed in proto3でエラーが生成されます。

syntax = "proto3";

message BaseMessage {
  extensions 100 to max;
}
16
Florian Wolters

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

私は知っていましたが、それはエレガントなソリューションではありませんが、シンプルで論理的です。

5

Extensions and Nested Extensions をチェックして、これを行うための少し簡潔な方法を確認してください。

2
bbudge

私にとっては、@ŁukaszMarciniakの答えが少し良い解決策です。

BarがFooを拡張する場合、単に次のように記述します。

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

したがって、Fooが進化した場合、Fooメッセージのみが変更されます。

0
Ekans

extensions の使用を検討しましたか? uriフィールドで使用するタイプを決定し、適切な拡張機能をロードするだけで済みます。フィールドが相互に排他的であることがわかっている場合は、別々の拡張子間でフィールドIDを再利用できます。

プロトコルバッファは、値の単純なリストを超えて自己記述できるように設計されていないため、これをすべて自分で処理する必要があります。これは、Googleテクニックのページで触れられています。

0
David Mitchell