Necesito especificar un mensaje con un campo opcional en protobuf (sintaxis proto3). En términos de sintaxis proto 2, el mensaje que quiero expresar es algo como:
message Foo {
required int32 bar = 1;
optional int32 baz = 2;
}
Según tengo entendido, el concepto "opcional" se ha eliminado de la sintaxis proto 3 (junto con el concepto obligatorio). Aunque no está clara la alternativa: usar el valor predeterminado para indicar que un campo no ha sido especificado por el remitente, deja una ambigüedad si el valor predeterminado pertenece al dominio de valores válidos (considere, por ejemplo, un tipo booleano).
Entonces, ¿cómo se supone que debo codificar el mensaje anterior? Gracias.
optional
Respuestas:
Desde la versión 3.12 de protobuf, proto3 ha admitido el uso de la
optional
palabra clave (al igual que en proto2) para proporcionar información de presencia de campo escalar.syntax = "proto3"; message Foo { int32 bar = 1; optional int32 baz = 2; }
Se genera un método
has_baz()
/hasBaz()
para eloptional
campo anterior, tal como estaba en proto2.Debajo del capó, el protocolo trata de manera efectiva un
optional
campo como si fuera declarado usando unoneof
contenedor, como sugiere la respuesta de CyberSnoopy :message Foo { int32 bar = 1; oneof optional_baz { int32 baz = 2; } }
Si ya ha utilizado ese enfoque, puede limpiar las declaraciones de sus mensajes (cambiar de
oneof
aoptional
), ya que el formato de cable es el mismo.Puede encontrar los detalles esenciales sobre la presencia de campo y
optional
en proto3 en la Nota de aplicación: documento de presencia de campo .En la versión 3.12, esta funcionalidad requiere pasar la
--experimental_allow_proto3_optional
bandera al protocolo. El anuncio de la función dice que estará "generalmente disponible con suerte en 3.13".Actualización de octubre de 2020: la función todavía se considera experimental (se requiere marca) en la versión 3.13 .
fuente
optional int xyz
: 1)has_xyz
detecta si el valor opcional se estableció 2)clear_xyz
anulará el valor. Más información aquí: github.com/protocolbuffers/protobuf/blob/master/docs/…En proto3, todos los campos son "opcionales" (en el sentido de que no es un error si el remitente no los configura). Pero los campos ya no son "anulables", ya que no hay forma de diferenciar entre un campo que se establece explícitamente en su valor predeterminado y que no se establece en absoluto.
Si necesita un estado "nulo" (y no hay un valor fuera de rango que pueda usar para esto), entonces deberá codificarlo como un campo separado. Por ejemplo, podrías hacer:
message Foo { bool has_baz = 1; // always set this to "true" when using baz int32 baz = 2; }
Alternativamente, puede usar
oneof
:message Foo { oneof baz { bool baz_null = 1; // always set this to "true" when null int32 baz_value = 2; } }
La
oneof
versión es más explícita y más eficiente en el cable, pero requiere comprender cómooneof
funcionan los valores.Finalmente, otra opción perfectamente razonable es seguir con proto2. Proto2 no está obsoleto y, de hecho, muchos proyectos (incluso dentro de Google) dependen en gran medida de las características de proto2 que se eliminan en proto3, por lo que es probable que nunca cambien. Por lo tanto, es seguro seguir usándolo en el futuro previsible.
fuente
Una forma es usar
oneof
como se sugiere en la respuesta aceptada.Otro es usar objetos envoltorios. No es necesario que los escriba usted mismo, ya que Google ya los proporciona:
En la parte superior de su archivo .proto agregue esta importación:
import "google/protobuf/wrappers.proto";
Ahora puede usar envoltorios especiales para cada tipo simple:
DoubleValue FloatValue Int64Value UInt64Value Int32Value UInt32Value BoolValue StringValue BytesValue
Entonces, para responder a la pregunta original, el uso de dicho contenedor podría ser así:
message Foo { int32 bar = 1; google.protobuf.Int32Value baz = 2; }
Ahora, por ejemplo, en Java puedo hacer cosas como:
if(foo.hasBaz()) { ... }
fuente
baz=null
y cuándobaz
no se pasa, ¡ambos casoshasBaz()
dicenfalse
!Según la respuesta de Kenton, una solución más simple pero funcional se ve así:
message Foo { oneof optional_baz { // "optional_" prefix here just serves as an indicator, not keyword in proto2 int32 baz = 1; } }
fuente
None
(en C #); consulte el tipo de enumeración para el idioma que elija.Para ampliar la sugerencia de @cybersnoopy aquí
si tenía un archivo .proto con un mensaje como este:
message Request { oneof option { int64 option_value = 1; } }
Puede hacer uso de las opciones de caso proporcionadas (código generado por Java) :
Entonces ahora podemos escribir un código de la siguiente manera:
Request.OptionCase optionCase = request.getOptionCase(); OptionCase optionNotSet = OPTION_NOT_SET; if (optionNotSet.equals(optionCase)){ // value not set } else { // value set }
fuente
Hay una buena publicación sobre esto: https://itnext.io/protobuf-and-null-support-1908a15311b6
La solución depende de su caso de uso real:
Manejo de actualización parcial
Soporte nulo
fuente
Otra forma es que puede usar máscara de bits para cada campo opcional. y establecer esos bits si se establecen valores y restablecer aquellos bits cuyos valores no se establecen
enum bitsV { baz_present = 1; // 0x01 baz1_present = 2; // 0x02 } message Foo { uint32 bitMask; required int32 bar = 1; optional int32 baz = 2; optional int32 baz1 = 3; }
Al analizar, compruebe el valor de bitMask.
if (bitMask & baz_present) baz is present if (bitMask & baz1_present) baz1 is present
fuente
puede encontrar si uno se ha inicializado comparando las referencias con la instancia predeterminada:
GRPCContainer container = myGrpcResponseBean.getContainer(); if (container.getDefaultInstanceForType() != container) { ... }
fuente