提问者:小点点

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


我正在尝试升级到使用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并向后兼容。


共1个答案

匿名用户

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

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 
}

generate代码使用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;
}
  • 请注意,其中一个的名称是任意的,我采用了约定fieldname\u present

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