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

Protocol buffers 将protobuf从版本2升级到版本3-与protobuf默认值不兼容,protocol-buffers,Protocol Buffers,我正在尝试升级到使用protobuf版本3,并与版本2保持向后兼容。除了一件事,它似乎可以工作——在proto-2中,您可以设置自己的默认值,但在proto-3中,您不能。如果您在proto-2中选择的默认值不是proto-3中的标准默认值,则您有问题。例如,在proto-2中: message Record { required uint32 fileno = 1; required uint64 pos = 2;

我正在尝试升级到使用protobuf版本3,并与版本2保持向后兼容。除了一件事,它似乎可以工作——在proto-2中,您可以设置自己的默认值,但在proto-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];  
}
现在在proto-3中必须是:

message Record {
  uint32 fileno = 1;               
  uint64 pos = 2;                  
  uint64 bmsPos = 3; 
  uint32 scanMode = 4;  
}
在proto-2和proto-3中,缺少的值不会在消息中发送。但是proto-3api没有告诉您消息中是否有默认值,它只是告诉您该值

因此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并向后兼容。

事实证明,有一种方法可以确定默认值是否确实丢失(感谢谷歌的一些朋友给出了这个答案):

generate代码使用getXXXcase()方法检测是否设置了其中一个字段:

  • 请注意,其中一个的名称是任意的,我采用了约定的fieldname_present
  • 其中之一不会向wire格式添加任何内容,因此它与proto-2消息保持兼容
  • 您可以在任何有意义的地方添加版本信息,我将其放在本例的记录消息中

有了这个“把戏”,我升级到了proto-3,向后兼容非标准proto-2默认值。

Nice trick!我是proto2(但不是proto3)的作者,我设计了“oneof”功能,但我不确定我是否会想到这一点!不知道是谁想出了这个把戏。谷歌的一个朋友帮我找到了它。我想其他人会需要它。@JohnCaron,我也在寻找同样的解决方案,如果我正确理解你的解决方案,它是有效的,但我有点害怕必须向所有客户端添加设置默认值的逻辑。所以基本上,其中之一是proto3版本的真正可选字段(例如,它不能设置),人们可以检测它是否被设置。
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 
}
int scanMode = proto.getScanMode();
boolean isMissing = proto.getScanModePresentCase() == Record.ScanModePresentCase.SCANMODEPRESENT_NOT_SET;
if (isMissing) {
  boolean isProto3 = proto.getVersion() >= 3;
  scanMode = (isProto3) ? 0 : 9999;
}