Изменение необязательного поля protobuff на oneof - PullRequest
2 голосов
/ 03 февраля 2020

У меня есть следующее сообщение:

message Message {
    int64 id = 1;
    google.protobuf.FloatValue weight = 2;
    google.protobuf.FloatValue override_weight = 3;
}

и я wi sh, чтобы изменить тип веса и override_weight (необязательные поля) на google.protobuf.DoubleValue, так что я сделал следующее:

message Message {
    int64 id = 1;
    oneof weight_oneof {
        google.protobuf.FloatValue weight = 2 [deprecated=true];
        google.protobuf.DoubleValue double_weight = 4;
    }
    oneof override_weight_oneof {
        google.protobuf.FloatValue override_weight = 3 [deprecated=true];
        google.protobuf.DoubleValue double_override_weight = 5;
    }
}

Мой вопрос: давайте предположим, что у меня есть старые сообщения, которые были скомпилированы предыдущим компилятором сообщений protobuff для старого сообщения, смогу ли я проанализировать их как новое сообщение? Документация очень расплывчата по этому поводу:

"Переместить необязательные поля в одно или из одного: вы можете потерять часть вашей информации (некоторые поля будут очищены) после сериализации и анализа сообщения. Однако вы может безопасно переместить одно поле в новое и может переместить несколько полей, если известно, что задано только одно. "

Кто-нибудь пробовал это раньше? Какова наилучшая практика для этой ситуации?

1 Ответ

2 голосов
/ 05 февраля 2020

Насколько я знаю, поля в oneof просто сериализуются с использованием их номера тега. Сериализованные данные не указывают, является ли поле частью oneof. Все это обрабатывается сериализатором и десериализатором. Таким образом, пока номера тегов не конфликтуют, можно предположить, что они будут работать в обоих направлениях: старые сообщения новому сериализатору и новые сообщения старому сериализатору.

Вы можете проверить это, используя онлайновый десериализатор protobuf .

Проверка. Код действительно выдает одинаковые байтовые строки. Ниже вы найдете определения сообщений и python код, который я использовал. Код python выведет строку байтов, которую вы можете скопировать и использовать в декодере Mar c Gravell.

syntax = "proto3";

message MessageA {
    int64 id = 1;
    float weight = 2;
    float override_weight = 3;
}

message MessageB {
    int64 id = 1;
    oneof weight_oneof {
        float weight = 2 [deprecated=true];
        double double_weight = 4;
    }
    oneof override_weight_oneof {
        float override_weight = 3 [deprecated=true];
        double double_override_weight = 5;
    }
}
import Example_pb2

# Set some data in the original message
msgA = Example_pb2.MessageA()
msgA.id = 1234
msgA.weight = 3.21
msgA.override_weight = 5.43

# Output the serialized bytes in a pretty format
str = 'msgA   = '
for x in msgA.SerializeToString():
    str += "{:02x} ".format(x)
print(str)

# Next set the original fields in the new message
msgB = Example_pb2.MessageB()
msgB.id = 1234
msgB.weight = 3.21
msgB.override_weight = 5.43

# Output the serialized bytes in a pretty format
str = 'msgB 1 = '
for x in msgB.SerializeToString():
    str += "{:02x} ".format(x)
print(str)

# And finally set the new fields in msgB
msgB.double_weight = 3.21
msgB.double_override_weight = 5.43

# Output the serialized bytes in a pretty format
str = 'msgB 2 = '
for x in msgB.SerializeToString():
    str += "{:02x} ".format(x)
print(str)

Вывод сценария python:

msgA   = 08 d2 09 15 a4 70 4d 40 1d 8f c2 ad 40
msgB 1 = 08 d2 09 15 a4 70 4d 40 1d 8f c2 ad 40
msgB 2 = 08 d2 09 21 ae 47 e1 7a 14 ae 09 40 29 b8 1e 85 eb 51 b8 15 40

Как видите, сообщение A и сообщение B выдают одинаковую строку байтов при установке исходных полей. Только когда вы устанавливаете новые поля, вы получаете другую строку.

...