web-dev-qa-db-ja.com

protobufのバージョン2から3へのアップグレード-protobufのデフォルト値との互換性なし

Protobufバージョン3を使用するようにアップグレードし、バージョン2との下位互換性を維持しようとしています。1つのことを除いて機能するようです。proto-2では独自のデフォルト値を設定できますが、proto 3ではできません。 proto-3の標準のデフォルト値ではないproto-2のデフォルト値を選択した場合、問題が発生します。たとえば、proto-2の場合:

message Record {
  required uint32 fileno = 1;               
  required uint64 pos = 2;                  
  optional uint64 bmsPos = 3 [default = 0]; 
  optional uint32 scanMode = 4 [default = 9999];  
}

現在、proto-3では次のようになります。

message Record {
  uint32 fileno = 1;               
  uint64 pos = 2;                  
  uint64 bmsPos = 3; 
  uint32 scanMode = 4;  
}

Proto-2とproto-3の両方で、欠損値がメッセージで送信されません。しかし、proto-3 APIは、デフォルト値がメッセージ内にあるかどうかを通知せず、値を通知するだけです。

したがって、proto-3受信者はメッセージを取得し、scanMode = 0であることを通知します。そのメッセージがproto-2送信者から来た場合は、1)proto-2送信者がメッセージに0を入力するか、2)proto- 2送信者が値を9999(デフォルト値)に設定しているため、値は送信されず、proto-3受信者はそれを0として解釈します。値がメッセージに存在するかどうかを知らないと、コードは明確になりません、メッセージがproto-2またはproto-3の送信者からのものかどうかがわかっていても。

Proto-2メッセージはproto-3(0)と同じデフォルト値を使用するため、この例のbmsPosフィールドには問題がないことに注意してください。ただし、proto-3とは異なるデフォルト値を選択した場合、proto-3にアップグレードして下位互換性を保つ方法はわかりません。

35
John Caron

デフォルト値が実際に欠落しているかどうかを確認する方法があることがわかりました(この答えについてはgoogleの友人に感謝します):

message Record {
  uint32 fileno = 1;               
  uint64 pos = 2;                  
  uint64 bmsPos = 3; 
  oneof scanMode_present {
    uint32 scanMode = 4;
  }
  uint32 version = 5; // set to >= 3 for protobuf 3 
}

生成コードにはoneofフィールドが設定されているかどうかを検出する追加メソッドがあり、getXXXcase()メソッドを使用します。

int scanMode = proto.getScanMode();
boolean isMissing = proto.getScanModePresentCase() == Record.ScanModePresentCase.SCANMODEPRESENT_NOT_SET;
if (isMissing) {
  boolean isProto3 = proto.getVersion() >= 3;
  scanMode = (isProto3) ? 0 : 9999;
}
  • oneofの名前は任意であることに注意してください。私はfieldname_presentという規則を採用しています。
  • oneofは、ワイヤー形式に何も追加しないため、proto-2メッセージとの互換性が維持されます。
  • version infoを意味のあるところならどこにでも追加できます。この例のRecordメッセージにそれを入れます。

この「トリック」により、非標準のproto-2デフォルト値との後方互換性を備えたproto-3にアップグレードしました。

41
John Caron