Muszę określić wiadomość z opcjonalnym polem w protobuf (składnia proto3). Jeśli chodzi o składnię Proto 2, komunikat, który chcę wyrazić, jest mniej więcej taki:
message Foo {
required int32 bar = 1;
optional int32 baz = 2;
}
Z mojego rozumienia „opcjonalne” pojęcie zostało usunięte ze składni proto 3 (wraz z wymaganym pojęciem). Chociaż nie jest jasna alternatywa - użycie wartości domyślnej do stwierdzenia, że pole nie zostało określone przez nadawcę, pozostawia niejednoznaczność, jeśli wartość domyślna należy do domeny prawidłowych wartości (na przykład typ boolowski).
Jak więc mam zakodować powyższą wiadomość? Dziękuję Ci.
optional
Odpowiedzi:
Od wersji 3.12 protobuf , proto3 ma eksperymentalne wsparcie dla użycia
optional
słowa kluczowego (tak jak w proto2) do podania skalarnej informacji o obecności pola.syntax = "proto3"; message Foo { int32 bar = 1; optional int32 baz = 2; }
has_baz()
/hasBaz()
Metoda jest generowany naoptional
obszarze powyżej, tak jak to było w proto2.Pod maską Protoc skutecznie traktuje
optional
pole tak, jakby zostało zadeklarowane przy użyciuoneof
opakowania, jak sugeruje odpowiedź CyberSnoopy :message Foo { int32 bar = 1; oneof optional_baz { int32 baz = 2; } }
Jeśli już zastosowałeś to podejście, będziesz w stanie wyczyścić deklaracje wiadomości (przełączyć się z
oneof
naoptional
), gdy obsługaoptional
protokołu proto3 przejdzie ze statusu eksperymentalnego, ponieważ format połączenia jest taki sam.Szczegółowe informacje dotyczące obecności
optional
w terenie oraz w protokole Proto3 można znaleźć w Nota aplikacyjna: Obecność w terenie doc.Przekaż
--experimental_allow_proto3_optional
flagę protokołowi, aby użyć tej funkcji w wersji 3.12. Ogłoszenie funkcji mówi, że będzie „ogólnie dostępne miejmy nadzieję w 3.13”.Aktualizacja z listopada 2020 r .: ta funkcja jest nadal uważana za eksperymentalną (wymagana flaga) w wersji 3.14 . Istnieją oznaki postępu.
źródło
optional int xyz
: 1)has_xyz
wykrywa, czy ustawiono opcjonalną wartość 2)clear_xyz
usunie wartość. Więcej informacji tutaj: github.com/protocolbuffers/protobuf/blob/master/docs/…W proto3 wszystkie pola są „opcjonalne” (nie jest to błąd, jeśli nadawca ich nie ustawi). Ale pola nie są już „dopuszczalne do wartości null”, ponieważ nie ma sposobu, aby odróżnić pole, które jest jawnie ustawione na wartość domyślną, a tym, że w ogóle nie zostało ustawione.
Jeśli potrzebujesz stanu „null” (i nie ma wartości spoza zakresu, której możesz użyć do tego), zamiast tego musisz zakodować to jako oddzielne pole. Na przykład możesz zrobić:
message Foo { bool has_baz = 1; // always set this to "true" when using baz int32 baz = 2; }
Alternatywnie możesz użyć
oneof
:message Foo { oneof baz { bool baz_null = 1; // always set this to "true" when null int32 baz_value = 2; } }
oneof
Wersja jest bardziej wyraźny i bardziej wydajny na drucie, ale wymaga zrozumienia, jakoneof
działa wartości.Wreszcie inną całkowicie rozsądną opcją jest trzymanie się proto2. Proto2 nie jest przestarzałe, a w rzeczywistości wiele projektów (w tym wewnątrz Google) bardzo zależy od funkcji proto2, które zostały usunięte w proto3, dlatego prawdopodobnie nigdy się nie przełączą. Dlatego można bezpiecznie używać go w dającej się przewidzieć przyszłości.
źródło
Jednym ze sposobów jest
optional
polubienie opisane w zaakceptowanej odpowiedzi: https://stackoverflow.com/a/62566052/1803821Innym jest użycie obiektów opakowujących. Nie musisz ich pisać samodzielnie, ponieważ Google już je udostępnia:
U góry pliku .proto dodaj ten import:
import "google/protobuf/wrappers.proto";
Teraz możesz użyć specjalnych opakowań dla każdego prostego typu:
DoubleValue FloatValue Int64Value UInt64Value Int32Value UInt32Value BoolValue StringValue BytesValue
Aby odpowiedzieć na pierwotne pytanie, użycie takiego opakowania mogłoby wyglądać tak:
message Foo { int32 bar = 1; google.protobuf.Int32Value baz = 2; }
Teraz na przykład w Javie mogę robić takie rzeczy jak:
if(foo.hasBaz()) { ... }
źródło
baz=null
i kiedybaz
nie jest zdany, oba przypadkihasBaz()
mówiąfalse
!W oparciu o odpowiedź Kentona, prostsze, ale działające rozwiązanie wygląda następująco:
message Foo { oneof optional_baz { // "optional_" prefix here just serves as an indicator, not keyword in proto2 int32 baz = 1; } }
źródło
None
(w C #) - zobacz typ wyliczenia dla wybranego języka.Aby rozwinąć sugestię @cybersnoopy tutaj
gdybyś miał plik .proto z taką wiadomością:
message Request { oneof option { int64 option_value = 1; } }
Możesz skorzystać z podanych opcji sprawy (kod wygenerowany w Javie) :
Więc możemy teraz napisać kod w następujący sposób:
Request.OptionCase optionCase = request.getOptionCase(); OptionCase optionNotSet = OPTION_NOT_SET; if (optionNotSet.equals(optionCase)){ // value not set } else { // value set }
źródło
Jest na ten temat dobry post: https://itnext.io/protobuf-and-null-support-1908a15311b6
Rozwiązanie zależy od rzeczywistego przypadku użycia:
Obsługa częściowej aktualizacji
Wsparcie null
źródło
Innym sposobem zakodowania zamierzonej wiadomości jest dodanie kolejnego pola do śledzenia pól „ustawionych”:
syntax="proto3"; package qtprotobuf.examples; message SparseMessage { repeated uint32 fieldsUsed = 1; bool attendedParty = 2; uint32 numberOfKids = 3; string nickName = 4; } message ExplicitMessage { enum PARTY_STATUS {ATTENDED=0; DIDNT_ATTEND=1; DIDNT_ASK=2;}; PARTY_STATUS attendedParty = 1; bool indicatedKids = 2; uint32 numberOfKids = 3; enum NO_NICK_STATUS {HAS_NO_NICKNAME=0; WOULD_NOT_ADMIT_TO_HAVING_HAD_NICKNAME=1;}; NO_NICK_STATUS noNickStatus = 4; string nickName = 5; }
Jest to szczególnie odpowiednie, jeśli istnieje duża liczba pól i tylko niewielka ich liczba została przypisana.
W Pythonie użycie wyglądałoby tak:
import field_enum_example_pb2 m = field_enum_example_pb2.SparseMessage() m.attendedParty = True m.fieldsUsed.append(field_enum_example_pb2.SparseMessages.ATTENDEDPARTY_FIELD_NUMBER)
źródło
Innym sposobem jest użycie maski bitowej dla każdego pola opcjonalnego. i ustaw te bity, jeśli wartości są ustawione, i zresetuj te bity, których wartości nie są ustawione
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; }
Podczas sprawdzania analizowania wartości parametru bitMask.
if (bitMask & baz_present) baz is present if (bitMask & baz1_present) baz1 is present
źródło
możesz sprawdzić, czy został zainicjowany, porównując odwołania z domyślną instancją:
GRPCContainer container = myGrpcResponseBean.getContainer(); if (container.getDefaultInstanceForType() != container) { ... }
źródło