web-dev-qa-db-fra.com

Mise à niveau de protobuf de la version 2 à 3 - incompatible avec les valeurs par défaut de protobuf

Im essayant de passer à l'utilisation de protobuf version 3 et de rester rétrocompatible avec la version 2. Semble fonctionner sauf pour une chose - dans proto-2, vous pouvez définir vos propres valeurs par défaut, mais dans proto 3, vous ne pouvez pas. Si vous avez choisi une valeur par défaut dans proto-2 qui n'est pas la valeur par défaut standard dans proto-3, vous avez un problème. Par exemple, dans proto-2:

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

maintenant dans proto-3 doit être:

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

Dans proto-2 et proto-3, les valeurs manquantes ne sont pas envoyées dans le message. Mais l'API proto-3 ne vous dit pas si la valeur par défaut est dans le message ou non, elle vous indique simplement la valeur.

Ainsi, le récepteur proto-3 reçoit un message et me dit que scanMode = 0. Si ce message provient d'un expéditeur proto-2, alors 1) l'expéditeur proto-2 a placé un 0 dans le message, ou 2) le proto- 2 expéditeur a défini la valeur sur 9999 (la valeur par défaut), et donc la valeur n'est pas envoyée, et le récepteur proto-3 l'interprète comme un 0. Sans savoir si la valeur est présente dans le message ou non, mon code ne peut pas ambiguïter , même s'il sait si le message provient d'un expéditeur proto-2 ou proto-3.

Notez qu'il n'y a aucun problème avec le champ bmsPos dans l'exemple, car le message proto-2 utilise la même valeur par défaut que proto-3 (0). Mais s'il vous arrivait d'avoir choisi une valeur par défaut différente de proto-3, alors je ne vois pas comment passer à proto-3 et être rétrocompatible.

35
John Caron

Il s'avère qu'il existe un moyen de savoir si une valeur par défaut est réellement manquante ou non (merci à certains amis de Google pour cette réponse):

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 
}

Le code de génération a des méthodes supplémentaires pour détecter si les champs oneof sont définis, en utilisant la méthode 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;
}
  • Notez que le nom du oneof est arbitraire, j'ai adopté la convention fieldname_present.
  • oneof n'ajoute rien au format Wire, il reste donc compatible avec les messages proto-2.
  • Vous pouvez ajouter les informations version partout où cela a du sens, je les mets dans le message d'enregistrement pour cet exemple.

Avec cette `` astuce '', j'ai mis à niveau vers proto-3 avec une compatibilité descendante avec des valeurs par défaut proto-2 non standard.

41
John Caron