Подтвердить что ты не робот

Обновление protobuf от версии 2 до 3 - несовместимо с значениями по умолчанию protobuf

Я пытаюсь перейти на версию protobuf версии 3 и оставаться в обратной совместимости с версией 2. Кажется, что работает, кроме одного - в прото-2 вы можете установить свои собственные значения по умолчанию, но в прото 3 вы не можете. Если вы выбрали значение по умолчанию в прото-2, которое не является стандартным значением по умолчанию для прото-3, у вас возникнет проблема. Например, в прото-2:

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

теперь в прото-3 должно быть:

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

Как в прото-2, так и в proto-3 в сообщении отправляются отсутствующие значения arent. Но proto-3 API не сообщает вам, есть ли значение по умолчанию в сообщении или нет, оно просто сообщает вам значение.

Итак, приемник proto-3 получает сообщение и сообщает мне, что scanMode = 0. Если это сообщение поступило от отправителя прото-2, то либо 1) отправитель прото-2 разместил 0 в сообщении, либо 2) отправитель proto-2 устанавливает значение 9999 (значение по умолчанию), поэтому значение не отправляется, и приемник proto-3 интерпретирует его как 0. Не зная, присутствует ли значение в сообщении или нет, код не может быть однозначным, даже если он знает, было ли сообщение отправлено от отправителя прото-2 или прото-3.

Обратите внимание, что в примере нет проблемы с полем bmsPos, поскольку сообщение proto-2 использует то же значение по умолчанию, что и proto-3 (0). Но если вы случайно выбрали значение по умолчанию не такое же, как у proto-3, то я не вижу, как обновиться до прото-3 и быть обратно совместимым.

4b9b3361

Ответ 1

Оказывается, есть способ узнать, действительно ли значение по умолчанию отсутствует или нет (спасибо некоторым друзьям в 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 
}

Генерирующий код имеет дополнительные методы для определения того, установлено ли одно из полей, используя метод 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.
  • Вы можете добавить информацию о версии в любом месте, что имеет смысл, я поместил ее в сообщение "Запись" для этого примера.

С помощью этого "трюка" я обновил до proto-3 с обратной совместимостью с нестандартными значениями прото-2 по умолчанию.