web-dev-qa-db-ja.com

protobuf 3でオプションのフィールドを定義する方法

Protobufのオプションフィールドでメッセージを指定する必要があります(proto3構文)。 proto 2構文に関して、表現したいメッセージは次のようなものです。

message Foo {
    required int32 bar = 1;
    optional int32 baz = 2;
}

私の理解では、「オプション」の概念は構文プロト3から削除されました(必須の概念とともに)。代替手段は明確ではありませんが、デフォルト値を使用して送信者からフィールドが指定されていないことを示すと、デフォルト値が有効な値ドメインに属する場合はあいまいなままになります(ブール型などを考慮してください)。

では、上記のメッセージをどのようにエンコードするのですか?ありがとうございました。

51
MaxP

Proto3では、すべてのフィールドは「オプション」です(送信者が設定に失敗してもエラーではないため)。しかし、フィールドは「nullable」ではなくなりました。デフォルト値に明示的に設定されているフィールドと設定されていないフィールドの違いを区別する方法がありません。

「null」状態が必要な場合(およびこれに使用できる範囲外の値がない場合)、代わりにこれを別のフィールドとしてエンコードする必要があります。たとえば、次のことができます。

message Foo {
  bool has_baz = 1;  // always set this to "true" when using baz
  int32 baz = 2;
}

または、 oneof を使用することもできます。

message Foo {
  oneof baz {
    bool baz_null = 1;  // always set this to "true" when null
    int32 baz_value = 2;
  }
}

oneofバージョンは、より明示的で効率的ですが、oneof値がどのように機能するかを理解する必要があります。

最後に、別の完全に合理的なオプションは、proto2に固執することです。 Proto2は非推奨ではありません。実際、多くのプロジェクト(Google内部を含む)はproto3で削除されたproto2機能に大きく依存しているため、切り替えられない可能性があります。そのため、近い将来に使用し続けることは安全です。

72
Kenton Varda

1つの方法は、受け入れられた回答で提案されているoneofを使用することです。

もう1つは、ラッパーオブジェクトを使用することです。 Googleが既に提供しているので、自分で作成する必要はありません。

.protoファイルの上部に、このインポートを追加します。

import "google/protobuf/wrappers.proto";

これで、すべての単純型に特別なラッパーを使用できます。

DoubleValue
FloatValue
Int64Value
UInt64Value
Int32Value
UInt32Value
BoolValue
StringValue
BytesValue

したがって、元の質問に答えるには、このようなラッパーの使用法は次のようになります。

message Foo {
    int32 bar = 1;
    google.protobuf.Int32Value baz = 2;
}

今、たとえばJavaで次のようなことができます:

if(foo.hasBaz()) { ... }

46
VM4

ケントンの答えに基づいて、よりシンプルでありながら機能するソリューションは次のようになります。

message Foo {
    oneof optional_baz { // "optional_" prefix here just serves as an indicator, not keyword in proto2
        int32 baz = 1;
    }
}
16
CyberSnoopy

@cybersnoopyの提案を拡張するには here

次のようなメッセージを含む.protoファイルがある場合:

message Request {
    oneof option {
        int64 option_value = 1;
    }
}

ケースオプションを利用できます 提供(Java生成コード)

したがって、次のようにコードを記述できます。

Request.OptionCase optionCase = request.getOptionCase();
OptionCase optionNotSet = OPTION_NOT_SET;

if (optionNotSet.equals(optionCase)){
    // value not set
} else {
    // value set
}
3

参照をデフォルトのインスタンスと比較することで、初期化されているかどうかを確認できます。

GRPCContainer container = myGrpcResponseBean.getContainer();
if (container.getDefaultInstanceForType() != container) {
...
}
0
eduyayo