web-dev-qa-db-fra.com

Comment définir un champ optionnel dans protobuf 3

Je dois spécifier un message avec un champ optionnel dans protobuf (syntaxe proto3). En ce qui concerne la syntaxe proto 2, le message que je veux exprimer ressemble à ceci:

message Foo {
    required int32 bar = 1;
    optional int32 baz = 2;
}

A ma connaissance, le concept "optionnel" a été supprimé de la syntaxe proto 3 (avec le concept requis). Bien que l'alternative ne soit pas claire - utiliser la valeur par défaut pour indiquer qu'un champ n'a pas été spécifié par l'expéditeur, laisse une ambiguïté si la valeur par défaut appartient au domaine des valeurs valides (considérons par exemple un type booléen).

Alors, comment suis-je censé encoder le message ci-dessus? Je vous remercie.

34
MaxP

Dans proto3, tous les champs sont "facultatifs" (en ce sens que l'expéditeur ne parvient pas à les définir). Mais les champs ne sont plus "nuls", en ce sens qu'il n'y a aucun moyen de faire la différence entre un champ explicitement défini sur sa valeur par défaut et le fait de ne pas l'avoir été du tout.

Si vous avez besoin d'un état "null" (et qu'il n'y a pas de valeur hors plage que vous pouvez utiliser pour cela), vous devrez plutôt l'encoder comme un champ séparé. Par exemple, vous pourriez faire:

message Foo {
  bool has_baz = 1;
  int32 baz = 2;
}

Vous pouvez aussi utiliser oneof :

message Foo {
  oneof baz {
    bool baz_null = 1;  // always set this to "true" when using
    int32 baz_value = 2;
  }
}

La version oneof est plus explicite et plus efficace sur le réseau, mais elle nécessite de comprendre comment les valeurs oneof fonctionnent.

Enfin, une autre option parfaitement raisonnable est de rester avec proto2. Proto2 n'est pas obsolète et de nombreux projets (y compris Google) dépendent en grande partie des fonctionnalités de proto2 qui sont supprimées dans proto3 et ne basculeront donc probablement jamais. Donc, il est prudent de continuer à l'utiliser dans un avenir proche.

51
Kenton Varda

D'après la réponse de Kenton, une solution simple mais efficace ressemble à ceci:

message Foo {
    oneof optional_baz { // "optional_" prefix here just serves as an indicator, not keyword in proto2
        int32 baz = 1;
    }
}
13
CyberSnoopy

Pour développer la suggestion de @cybersnoopy ici

si vous aviez un fichier .proto avec un message comme celui-ci:

message Request {
    oneof option {
        int64 option_value = 1;
    }
}

Vous pouvez utiliser les options de cas fournies (code généré par Java) :

Donc, nous pouvons maintenant écrire du code comme suit:

Request.OptionCase optionCase = request.getOptionCase();
OptionCase optionNotSet = OPTION_NOT_SET;

if (optionNotSet.equals(optionCase)){
    // value not set
} else {
    // value set
}
2
Benjamin Slabbert

vous pouvez trouver si l'un a été initialisé en comparant les références avec l'instance par défaut:

GRPCContainer container = myGrpcResponseBean.getContainer();
if (container.getDefaultInstanceForType() != container) {
...
}
0
eduyayo