2015-10-18 39 views
25

Cố gắng nâng cấp lên bằng phiên bản protobuf 3, và tương thích ngược với phiên bản 2. Dường như hoạt động ngoại trừ một điều - trong proto-2 bạn có thể đặt các giá trị mặc định của riêng, nhưng trong proto 3, bạn không thể. Nếu bạn đã chọn một giá trị mặc định trong proto-2 không phải là giá trị mặc định chuẩn trong proto-3, thì bạn có một vấn đề. Ví dụ, trong proto-2:Nâng cấp protobuf từ phiên bản 2 lên 3 - không tương thích với các giá trị mặc định protobuf

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

bây giờ trong proto-3 phải là:

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

Trong cả hai proto-2 và proto-3, các giá trị thiếu arent gửi trong tin nhắn. Nhưng API proto-3 không cho bạn biết nếu giá trị mặc định nằm trong tin nhắn hay không, nó chỉ cho bạn biết giá trị.

Vì vậy, người nhận proto-3 nhận được tin nhắn và cho tôi biết rằng scanMode = 0. Nếu thông báo đó đến từ người gửi proto-2, thì 1) người gửi proto-2 đặt 0 trong tin nhắn hoặc 2) người gửi proto-2 thiết lập giá trị thành 9999 (giá trị mặc định), và do đó giá trị không được gửi và bộ nhận proto-3 diễn giải nó là 0. Nếu không biết liệu giá trị có xuất hiện trong tin nhắn hay không, mã của tôi không thể phân biệt, ngay cả khi nó biết liệu thông báo đến từ một người gửi proto-2 hay proto-3.

Lưu ý rằng không có vấn đề gì với trường bmsPos trong ví dụ, vì thông điệp proto-2 sử dụng cùng giá trị mặc định như proto-3 (0). Nhưng nếu bạn tình cờ đã chọn một giá trị mặc định không giống như proto-3, thì tôi không thấy cách nâng cấp lên proto-3 và tương thích ngược.

Trả lời

31

Hóa ra đó là một cách để tìm hiểu xem một giá trị mặc định là thực sự thiếu hoặc không (nhờ một số người bạn tại google cho câu trả lời này):

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 
} 

Các tạo mã có phương pháp bổ sung để phát hiện nếu oneof lĩnh vực được thiết lập, sử dụng getXXXcase() phương pháp:

int scanMode = proto.getScanMode(); 
boolean isMissing = proto.getScanModePresentCase() == Record.ScanModePresentCase.SCANMODEPRESENT_NOT_SET; 
if (isMissing) { 
    boolean isProto3 = proto.getVersion() >= 3; 
    scanMode = (isProto3) ? 0 : 9999; 
} 
  • Lưu ý rằng tên của oneof là tùy ý, tôi đã chấp nhận quy ước fieldname_present.
  • oneof không thêm bất kỳ thứ gì vào định dạng dây, do đó, nó vẫn tương thích với các thư proto-2.
  • Bạn có thể thêm thông tin vào bất kỳ nơi nào có ý nghĩa, tôi đặt nó vào thông báo Bản ghi cho ví dụ này.

Với 'thủ thuật' này, tôi đã nâng cấp lên proto-3 với khả năng tương thích ngược với các giá trị mặc định proto-2 không chuẩn.

+16

Bí quyết tuyệt vời! Tôi là tác giả của proto2 (nhưng không phải proto3) và tôi đã thiết kế tính năng "oneof", nhưng tôi không chắc liệu tôi có nghĩ về điều này không! –

+1

Không biết ai đã tìm ra mẹo. Một người bạn từ google đã theo dõi nó cho tôi. Nghĩ rằng người khác sẽ cần nó. –

+0

@JohnCaron, tôi đang tìm kiếm tương tự, Nếu tôi hiểu giải pháp của bạn hiệu chỉnh nó hoạt động, nhưng tôi hơi sợ phải thêm tất cả các khách hàng logic để đặt giá trị mặc định. –

Các vấn đề liên quan