2015-10-18 16 views
25

Próbuję dokonać aktualizacji do wersji 3 protobuf i zachowuję zgodność wsteczną z wersją 2. Wydaje się działać z wyjątkiem jednej rzeczy - w proto-2 możesz ustawić własne wartości domyślne, ale w proto 3, nie możesz. Jeśli wybrałeś domyślną wartość w proto-2, która nie jest standardową wartością domyślną w proto-3, masz problem. Na przykład, w proto-2:Aktualizacja protobuf z wersji 2 na 3 - niezgodna z domyślnymi wartościami protobuf

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

teraz w proto-3 musi być:

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

zarówno w proto-2 i proto-3, brakujące wartości arent wysłany w wiadomości. Ale proto-3 API nie mówi ci, czy domyślna wartość jest w wiadomości czy nie, po prostu mówi ci wartość.

Tak więc odbiornik proto-3 otrzymuje wiadomość i mówi mi, że scanMode = 0. Jeśli ta wiadomość pochodzi od nadawcy proto-2, to 1) nadawca proto-2 umieścił 0 w wiadomości lub 2) nadawca proto-2 ustawił wartość na 9999 (wartość domyślna), więc wartość nie jest wysyłana, a odbiornik proto-3 interpretuje ją jako 0. Bez znajomości, czy wartość jest obecna w komunikacie, czy nie, mój kod nie może ujednoznacznić, nawet jeśli wie, czy wiadomość pochodzi od nadawcy proto-2 czy też od proto-3.

Należy zauważyć, że nie ma problemu z polem bmsPos w tym przykładzie, ponieważ komunikat proto-2 używa tej samej wartości domyślnej co proto-3 (0). Ale jeśli zdarzyło ci się, że wybrałeś domyślną wartość nie taką samą jak proto-3, to nie widzę jak uaktualnić proto-3 i być kompatybilne wstecz.

Odpowiedz

31

Okazuje się, że istnieje sposób, aby dowiedzieć się, czy wartość domyślna jest rzeczywiście brakuje lub nie (dzięki kilku znajomych w Google dla tej odpowiedzi):

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 
} 

generate kod ma dodatkowe metody do wykrywania jeśli oneof pola są ustawiane przy użyciu getXXXcase() metoda:

int scanMode = proto.getScanMode(); 
boolean isMissing = proto.getScanModePresentCase() == Record.ScanModePresentCase.SCANMODEPRESENT_NOT_SET; 
if (isMissing) { 
    boolean isProto3 = proto.getVersion() >= 3; 
    scanMode = (isProto3) ? 0 : 9999; 
} 
  • pamiętać, że nazwa oneof jest arbitralny, przyjęłam konwencję fieldname_present.
  • Jeden z nie dodaje niczego do formatu drutu, dzięki czemu pozostaje kompatybilny z wiadomościami proto-2.
  • Możesz dodać wersja informacji w dowolnym miejscu, które ma sens, i umieścić go w wiadomości Record dla tego przykładu.

Dzięki tej "sztuczce" zaktualizowałem proto-3 do kompatybilności wstecznej z niestandardowymi standardowymi proto-2.

+16

Dobra sztuczka! Jestem autorem proto2 (ale nie proto3) i zaprojektowałem funkcję "oneof", ale nie jestem pewien, czy o tym pomyślałem! –

+1

Nie wiem, kto wymyślił tę sztuczkę. Znajomy z google wyszukał go dla mnie. Myśl, że inni będą tego potrzebować. –

+0

@JohnCaron, szukałem tego samego, Jeśli rozumiem, że twoje rozwiązanie działa poprawnie, ale jestem trochę przerażony, że muszę dodać do wszystkich klientów logikę, aby ustawić domyślne wartości. –

Powiązane problemy