Umgang mit Nullwerten in Protobuffern

Lesezeit: 5 Minuten

Benutzer-Avatar
Aarish Ramesh

Ich arbeite an etwas, das Daten aus der Datenbank abruft und eine Protobuff-Nachricht erstellt. Angesichts der Möglichkeit, dass für bestimmte Felder Nullwerte aus der Datenbank abgerufen werden können, erhalte ich eine Nullzeiger-Ausnahme, während ich versuche, die protobuff-Nachricht zu erstellen. Erfahren Sie, dass null in Protobuffs aus dem Thread nicht unterstützt wird http://code.google.com/p/protobuf/issues/detail?id=57ich frage mich, ob die einzige andere Möglichkeit, mit dem Auslösen von NPE umzugehen, darin besteht, manuelle Prüfungen in die Java-Datei einzufügen, die dem Proto entspricht, wie unten!

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());
...

Kann also bitte jemand klären, ob es einen anderen möglichen effizienten Weg gibt, um die Nullwerte während der Protobuff-Konstruktion zu handhaben?

Benutzer-Avatar
Michael Xin Sonne

Haftungsausschluss: Antwort von einem Googler, der täglich Protobufs verwendet. Ich vertrete Google in keiner Weise.

  1. Benennen Sie Ihren Prototyp Person Anstatt von PersonProto oder ProtoPerson. Kompilierte Protobufs sind nur Klassendefinitionen, die von der von Ihnen verwendeten Sprache angegeben werden, mit einigen Verbesserungen. Das Hinzufügen von “Proto” ist zusätzliche Ausführlichkeit.
  2. Verwenden YourMessage.hasYourField() Anstatt von YourMessage.getYourField() != null. Der Standardwert für die protobuf-Zeichenfolge ist eine leere Zeichenfolge, was der Fall ist NICHT gleich Null. Unabhängig davon, ob Ihr Feld nicht festgelegt oder gelöscht oder eine leere Zeichenfolge ist, .hasYourField() gibt immer false zurück. Sehen Standardwerte für allgemeine Protobuf-Feldtypen.
  3. Sie haben es wahrscheinlich gewusst, aber ich möchte ausdrücklich sagen: Legen Sie kein protobuf-Feld programmgesteuert fest null. Auch außerhalb von Protobuf, null verursacht allerlei Probleme. Verwenden .clearYourField() stattdessen.
  4. Person.Builder Klasse tut NICHT haben eine .newBuilder() Methode. Person Klasse tut. Verstehe die Builder-Muster so: Sie erstellen einen neuen Builder nur, wenn Sie ihn noch nicht haben.

Eine Umschreibung Ihres Protobuf:

message Person {
  optional string first_name = 1;
  optional string last_name = 2;
  optional string address_1 = 3;
}

Eine Umschreibung Ihrer Logik:

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();

Und wenn thatPerson ein von Ihnen erstelltes Personenobjekt mit Attributwerten ist, die eine leere Zeichenfolge, Leerzeichen oder null sein können, dann würde ich die Verwendung empfehlen Guaven Strings Bibliothek:

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();

  • Ihr Beitrag mag voller guter Ratschläge sein … aber ich bin mir nicht sicher, ob Sie seine Frage verstanden haben? Beispielsweise stammen seine Daten nicht von einem anderen Protobuf-Objekt; Es stammt aus einer Datenbank oder einer anderen Quelle und kann Nullen enthalten. Er wollte im Grunde wissen, wie man mit den Nullen einfach umgeht.

    – Erhannis

    11. August 2016 um 21:35 Uhr

  • Guter Punkt Erhannis, danke. Ich habe die Antwort aktualisiert, indem ich einen Abschnitt hinzugefügt habe, der sich mit Objekten ohne Protokollpuffer befasst, die leere Zeichenfolgenwerte, aufeinanderfolgende Leerzeichenwerte und Nullwerte enthalten.

    – Michael Xin Sonne

    22. September 2017 um 2:08 Uhr

  • Der Link ‘verursacht alle möglichen Probleme’ ist tot, und ich würde wirklich gerne mehr erfahren

    – kinbiko

    2. November 2017 um 9:42 Uhr

  • @kinbiko: Link korrigiert. Guava wurde von Google Code zu GitHub migriert, daher jetzt der neue Link. Danke, dass du mich informiert hast!

    – Michael Xin Sonne

    2. November 2017 um 18:37 Uhr


  • Ist diese Antwort nicht genauso wie in der Frage vorgeschlagen – verwenden Sie einfach Nullprüfungen? Der relevante Teil – der Teil, der die Frage beantwortet, befindet sich nur im letzten Absatz. Verwirrenderweise besteht der andere Teil der Antwort darin, Fragen zu beantworten, die nicht gestellt wurden.

    – alex.p

    28. Mai 2019 um 10:31 Uhr

Benutzer-Avatar
Serj-Tm

Prototyp 3

wrappers.proto unterstützt Nullable-Werte:

  • string(StringWert),
  • int(Int32Wert),
  • bool(BoolWert)
  • und ETC

Beispiel

syntax = "proto3";
import "google/protobuf/wrappers.proto";

message ProtoPerson {
    google.protobuf.StringValue firstName = 1;
    google.protobuf.StringValue lastName = 2;
    google.protobuf.StringValue address1 = 3;
    google.protobuf.Int32Value age = 4;
}

  • Gibt es einen ähnlichen Weg für Nicht-Basistypen?

    – gpo

    17. April 2020 um 16:16 Uhr

  • @muslidrikk Nicht grundlegende Typen sind standardmäßig nullable

    – Serj-Tm

    27. April 2020 um 12:12 Uhr

  • Vollständige Liste hier docs.microsoft.com/en-us/dotnet/architecture/…

    – Shardul

    13. Oktober 2020 um 18:59 Uhr

Dafür gibt es keine einfache Lösung. Ich würde empfehlen, sich nur mit den Nullprüfungen zu befassen. Aber wenn Sie sie wirklich loswerden wollen, hier sind ein paar Ideen:

  • Du könntest a schreiben Code-Generator-Plugin was hinzufügt setOrClearFoo() Methoden zu jeder Java-Klasse. Der Java-Code-Generator bietet Einfügepunkte dafür (siehe Ende dieser Seite).
  • Sie könnten Java-Reflektion verwenden, um über die zu iterieren get*() Methoden von prufen Sie jeden an, suchen Sie nach nullund rufen Sie dann die an set*() Methode von builder wenn nicht null. Dies hat den zusätzlichen Vorteil, dass Sie Ihren Kopiercode nicht jedes Mal aktualisieren müssen, wenn Sie ein neues Feld hinzufügen, aber es ist viel langsamer als das Schreiben von Code, der jedes Feld explizit kopiert.

  • Bitte werfen Sie einen Blick auf diese Frage stackoverflow.com/questions/21474024/…

    – Aarish Ramesh

    31. Januar 2014 um 6:45 Uhr

  • Hat jemand tatsächlich ein Code-Generator-Plugin erstellt, das setOrClearFoo() ausführt oder ein Beispiel liefert?

    – Erik Englund

    6. Dezember 2018 um 16:51 Uhr

  • Ich habe Reflektion versucht, aber die Leistung nimmt einen steilen Sprung, wenn der Proto groß wird und Sie den Proto-Objekten einen großen Datensatz zuordnen müssen.

    – Dharmvir Tiwari

    15. Oktober 2021 um 18:55 Uhr


1145190cookie-checkUmgang mit Nullwerten in Protobuffern

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy