web-dev-qa-db-fra.com

Gestion des valeurs nulles dans les protobuffers

Je travaille sur quelque chose qui récupère les données de la base de données et construit un message protobuff. Étant donné la possibilité que des valeurs nulles puissent être extraites de la base de données pour certains champs, j'obtiendrai une exception Null-pointer tout en essayant de construire le message protobuff. Apprendre à savoir que null n'est pas pris en charge dans les protobuffs du fil http://code.google.com/p/protobuf/issues/detail?id=57 , je me demande si la seule autre façon pour gérer le lancement de NPE, c'est insérer des vérifications manuelles dans le fichier Java correspondant au proto comme ci-dessous!

message ProtoPerson{
    optional string firstName = 1;
    optional string lastName = 2;
    optional string address1 = 3;
}

ProtoPerson.Builder builder = ProtoPerson.Builder.newBuilder();
if (p.getFirstName() != null) builder.setFirstName(p.getFirstName());
if (p.getLastName() != null) builder.setLastName(p.getLastName());
if (p.getAddress1() != null) builder.setAddress1(p.getAddress1());
...

Alors, quelqu'un peut-il préciser s'il existe un autre moyen efficace de gérer les valeurs nulles lors de la construction du protobuff ??

32
Aarish Ramesh

Il n'y a pas de solution simple à cela. Je recommanderais simplement de traiter les contrôles nuls. Mais si vous voulez vraiment vous en débarrasser, voici quelques idées:

  • Vous pouvez écrire un plugin générateur de code qui ajoute des méthodes setOrClearFoo() à chaque classe Java. Le code Java générateur fournit points d'insertion pour cela (voir la fin de cette page).
  • Vous pouvez utiliser Java réflexion pour parcourir les méthodes get*() de p, appeler chacune d'elles, rechercher null, puis appeler la méthode set*() de builder si non nulle. Cela aura l'avantage supplémentaire que vous n'aurez pas à mettre à jour votre code de copie chaque fois que vous ajouterez un nouveau champ, mais ce sera beaucoup plus lent que l'écriture de code qui copie explicitement chaque champ.
11
Kenton Varda

Avertissement: réponse d'un googleur utilisant quotidiennement des protobufs. Je ne représente en aucun cas Google.

  1. Nommez votre proto Person au lieu de PersonProto ou ProtoPerson. Les protobufs compilés ne sont que des définitions de classe spécifiées par le langage que vous utilisez, avec quelques améliorations. L'ajout de "Proto" est une verbosité supplémentaire.
  2. Utilisez YourMessage.hasYourField() au lieu de YourMessage.getYourField() != null. La valeur par défaut de la chaîne de protobuf est une chaîne vide, qui [~ # ~] n'est pas [~ # ~] égale à null. Alors que votre champ est non défini ou effacé ou vide, .hasYourField() retourne toujours false. Voir valeurs par défaut pour les types de champs protobuf courants .
  3. Vous le savez probablement, mais je veux dire explicitement: Ne définissez pas par programmation un champ protobuf sur null. Même pour l'extérieur de protobuf, nullcause toutes sortes de problèmes . Utilisez à la place .clearYourField().
  4. La classe Person.Builder A [~ # ~] pas [~ # ~] une méthode .newBuilder(). Person classe le fait. Comprenez le Modèle de générateur comme ceci: vous créez un nouveau générateur uniquement si vous ne l'avez pas encore.

Une réécriture de votre protobuf:

message Person {
  optional firstName = 1;
  optional lastName = 2;
  optional address1 = 3;
}

Une réécriture de votre logique:

Person thatPerson = Person.newBuilder()
    .setFirstName("Aaa")
    .setLastName("Bbb")
    .setAddress1("Ccc")
    .build();

Person.Builder thisPersonBuilder = Person.newBuilder()

if (thatPerson.hasFirstName()) {
  thisPersonBuilder.setFirstName(thatPerson.getFirstName());
}

if (thatPerson.hasLastName()) {
  thisPersonBuilder.setLastName(thatPerson.getLastName());
}

if (thatPerson.hasAddress1()) {
  thisPersonBuilder.setAddress1(thatPerson.getAddress1());
}

Person thisPerson = thisPersonBuilder.build();

Et si thatPerson est un objet personne que vous avez créé et qui a des valeurs d'attribut qui pourraient être une chaîne vide, des espaces vides ou null, alors je recommanderais d'utiliser la bibliothèque Strings de Guava :

import static com.google.common.base.Strings.nullToEmpty;

Person.Builder thisPersonBuilder = Person.newBuilder()

if (!nullToEmpty(thatPerson.getFirstName()).trim().isEmpty()) {
  thisPersonBuilder.setFirstName(thatPerson.getFirstName());
}

if (!nullToEmpty(thatPerson.hasLastName()).trim().isEmpty()) {
  thisPersonBuilder.setLastName(thatPerson.getLastName());
}

if (!nullToEmpty(thatPerson.hasAddress1()).trim().isEmpty()) {
  thisPersonBuilder.setAddress1(thatPerson.getAddress1());
}

Person thisPerson = thisPersonBuilder.build();
43
Michael Xin Sun