web-dev-qa-db-ja.com

プロトバッファーでのヌル値の処理

私はデータベースからデータを取得してprotobuffメッセージを作成するものに取り組んでいます。特定のフィールドのデータベースからnull値を取得できる可能性を考えると、protobuffメッセージを作成しようとするとNull-pointer例外が発生します。スレッドからのプロトバフではnullがサポートされていないことを知る http://code.google.com/p/protobuf/issues/detail?id=57 、他に唯一​​の方法があるのかスローされるNPEを処理するには、以下のようなプロトタイプに対応するJavaファイルに手動チェックを挿入します!

message ProtoPerson{
    optional string firstName = 1;
    optional string lastName = 2;
    optional string address1 = 3;
}

ProtoPerson.Builder builder = ProtoPerson.Builder.newBuilder();
if (p.getFirstName() != null) builder.setFirstName(p.getFirstName());
if (p.getLastName() != null) builder.setLastName(p.getLastName());
if (p.getAddress1() != null) builder.setAddress1(p.getAddress1());
...

だから誰かが、プロトバフ構築中にnull値を処理する他の可能な効率的な方法があるかどうかを明確にすることができますか?

32
Aarish Ramesh

これには簡単な解決策はありません。 nullチェックを処理することをお勧めします。しかし、本当にそれらを取り除きたい場合は、いくつかのアイデアがあります:

  • 各JavaクラスにsetOrClearFoo()メソッドを追加する code generatorプラグイン を書くことができます。 Javaコードジェネレーターは、このために 挿入ポイント を提供します(そのページの終わりを参照)。
  • Javaリフレクションを使用して、pget*()メソッドを反復処理し、それぞれを呼び出し、nullを確認してから呼び出すことができます。 builderset*()メソッド(null以外)これには、新しいフィールドを追加するたびにコピーコードを更新する必要がないという利点がありますが、各フィールドを明示的にコピーするコードを記述するよりもはるかに遅くなります。
11
Kenton Varda

免責事項:毎日protobufsを使用してGoogle社員から回答します。私は決してGoogleを代表するものではありません。

  1. プロトにPersonまたはPersonProtoではなくProtoPersonと名前を付けます。コンパイルされたprotobufsは、使用している言語によって指定されたクラス定義であり、いくつかの改善が加えられています。 「Proto」を追加すると、さらに冗長になります。
  2. YourMessage.hasYourField()の代わりにYourMessage.getYourField() != nullを使用します。 protobuf文字列のデフォルト値は空の文字列で、[〜#〜] not [〜#〜]nullと等しくなります。一方、フィールドが設定されていないか、クリアされているか空の文字列であるかに関係なく、.hasYourField()は常にfalseを返します。一般的なprotobufフィールドタイプの default値 を参照してください。
  3. おそらく知っているでしょうが、私は明示的に言いたい:プロトブフフィールドをnullにプログラムで設定しないでください。プロトブフ以外でも、nullあらゆる種類の問題を引き起こします 。代わりに.clearYourField()を使用してください。
  4. _Person.Builder_クラスには[〜#〜] not [〜#〜]には.newBuilder()メソッドがありません。 Personクラスが行います。 Builderパターンを次のように理解します。新しいビルダーは、まだ作成していない場合にのみ作成します。

Protobufの書き換え:

_message Person {
  optional firstName = 1;
  optional lastName = 2;
  optional address1 = 3;
}
_

ロジックの書き換え:

_Person thatPerson = Person.newBuilder()
    .setFirstName("Aaa")
    .setLastName("Bbb")
    .setAddress1("Ccc")
    .build();

Person.Builder thisPersonBuilder = Person.newBuilder()

if (thatPerson.hasFirstName()) {
  thisPersonBuilder.setFirstName(thatPerson.getFirstName());
}

if (thatPerson.hasLastName()) {
  thisPersonBuilder.setLastName(thatPerson.getLastName());
}

if (thatPerson.hasAddress1()) {
  thisPersonBuilder.setAddress1(thatPerson.getAddress1());
}

Person thisPerson = thisPersonBuilder.build();
_

thatPersonが、空の文字列、空のスペース、またはnullの属性値を持つ作成した人物オブジェクトである場合、 GuavaのStrings library を使用することをお勧めします。

_import static com.google.common.base.Strings.nullToEmpty;

Person.Builder thisPersonBuilder = Person.newBuilder()

if (!nullToEmpty(thatPerson.getFirstName()).trim().isEmpty()) {
  thisPersonBuilder.setFirstName(thatPerson.getFirstName());
}

if (!nullToEmpty(thatPerson.hasLastName()).trim().isEmpty()) {
  thisPersonBuilder.setLastName(thatPerson.getLastName());
}

if (!nullToEmpty(thatPerson.hasAddress1()).trim().isEmpty()) {
  thisPersonBuilder.setAddress1(thatPerson.getAddress1());
}

Person thisPerson = thisPersonBuilder.build();
_
43
Michael Xin Sun