将protobuf从版本2升级到3 - 与protobuf默认值不兼容

Joh*_*ron 36 protocol-buffers

我试图升级到使用protobuf版本3,并保持向后兼容版本2.似乎工作除了一件事 - 在proto-2你可以设置自己的默认值,但在原型3,你不能.如果您在proto-2中选择的默认值不是proto-3中的标准默认值,那么您就遇到了问题.例如,在proto-2中:

message Record {
  required uint32 fileno = 1;               
  required uint64 pos = 2;                  
  optional uint64 bmsPos = 3 [default = 0]; 
  optional uint32 scanMode = 4 [default = 9999];  
}
Run Code Online (Sandbox Code Playgroud)

现在在proto-3必须是:

message Record {
  uint32 fileno = 1;               
  uint64 pos = 2;                  
  uint64 bmsPos = 3; 
  uint32 scanMode = 4;  
}
Run Code Online (Sandbox Code Playgroud)

在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发送者.

请注意,示例中的bmsPos字段没有问题,因为proto-2消息使用与proto-3(0)相同的默认值.但如果您碰巧选择了与proto-3不同的默认值,那么我就不会看到如何升级到proto-3并向后兼容.

Joh*_*ron 41

事实证明有一种方法可以确定默认值是否实际丢失(感谢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 
}
Run Code Online (Sandbox Code Playgroud)

的生成代码具有额外的方法来检测,如果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;
}
Run Code Online (Sandbox Code Playgroud)
  • 请注意,oneof的名称是任意的,我采用了惯例fieldname_present.
  • oneof犯规添加任何电线格式,因此它保持与原2的消息兼容.
  • 您可以在任何有意义的地方添加版本信息,我将其放在此示例的Record消息中.

有了这个'技巧',我已经升级到proto-3,向后兼容非标准的proto-2默认值.

  • 好戏!我是proto2的作者(但不是proto3),我设计了"oneof"功能,但我不确定我是否会想到这个! (23认同)