2015-10-18 1 views
25

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

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 и прото-3, пропущенных значениях Арента посланы в сообщении. Но proto-3 API не сообщает вам, есть ли значение по умолчанию в сообщении или нет, оно просто сообщает вам значение.

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

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

ответ

31

Оказывается, есть способ узнать, если значение по умолчанию, на самом деле отсутствует или нет (благодаря некоторым друзьям в 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; 
} 
  • Обратите внимание, что название one является произвольным, я принял конвенцию fieldname_present.
  • one не добавляет ничего в формат проводов, поэтому он остается совместимым с сообщениями proto-2.
  • Вы можете добавить версию информацию в любом месте, что имеет смысл, и я помещаю его в сообщение «Запись» для этого примера.

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

+16

Приятный трюк! Я автор proto2 (но не proto3), и я разработал функцию «oneof», но я не уверен, думал ли я об этом! –

+1

Не знаю, кто придумал трюк. Друг из Google отслеживал это для меня. Думали, что это понадобятся другим. –

+0

@JohnCaron, я искал то же самое, если я правильно понимаю ваше решение, это работает, но я немного боюсь необходимости добавлять ко всем клиентам логику для установки значений по умолчанию. –

Смежные вопросы