protocol buffers - google - Actualización de protobuf de la versión 2 a 3: incompatible con los valores predeterminados de protobuf
google:: protobuf (1)
Estoy intentando actualizar a usar protobuf versión 3, y seguir siendo compatible con la versión 2. Parece que funciona excepto por una cosa: en proto-2 puedes establecer tus propios valores predeterminados, pero en proto 3, no puedes. Si eligió un valor predeterminado en proto-2 que no es el valor predeterminado estándar en proto-3, entonces tiene un problema. Por ejemplo, en proto-2:
message Record {
required uint32 fileno = 1;
required uint64 pos = 2;
optional uint64 bmsPos = 3 [default = 0];
optional uint32 scanMode = 4 [default = 9999];
}
ahora en proto-3 debe ser:
message Record {
uint32 fileno = 1;
uint64 pos = 2;
uint64 bmsPos = 3;
uint32 scanMode = 4;
}
Tanto en proto-2 como en proto-3, los valores faltantes no se envían en el mensaje. Pero la API proto-3 no le dice si el valor predeterminado está en el mensaje o no, simplemente le dice el valor.
Entonces el receptor proto-3 recibe un mensaje y me dice que scanMode = 0. Si ese mensaje vino de un remitente proto-2, entonces 1) el remitente proto-2 colocó un 0 en el mensaje, o 2) el proto- 2 el remitente establece el valor en 9999 (el valor predeterminado), por lo que el valor no se envía y el receptor proto-3 lo interpreta como un 0. Sin saber si el valor está presente en el mensaje o no, mi código no puede ser ambiguo. , incluso si sabe si el mensaje proviene de un remitente proto-2 o proto-3.
Tenga en cuenta que no hay ningún problema con el campo bmsPos en el ejemplo, ya que el mensaje proto-2 usa el mismo valor predeterminado que proto-3 (0). Pero si ha elegido un valor predeterminado que no es el mismo que proto-3, entonces no veo cómo actualizar a proto-3 y ser compatible con versiones anteriores.
Resulta que hay una manera de averiguar si falta un valor predeterminado o no (gracias a algunos amigos de google por esta respuesta):
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
}
El código generado tiene métodos adicionales para detectar si uno de los campos está configurado, usando el método 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;
}
- Tenga en cuenta que el nombre de oneof es arbitrario, he adoptado la convención fieldname_present .
- El oneof no agrega nada al formato de cable, por lo que sigue siendo compatible con los mensajes proto-2.
- Puede agregar la información de la versión en cualquier lugar que tenga sentido, la puse en el mensaje de registro para este ejemplo.
Con este ''truco'', actualicé a proto-3 con compatibilidad hacia atrás con valores proto-2 no estándar.