将 protobuf 可选字段更改为其中一个

Changing protobuff optional field to oneof

我有以下消息:

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

并且我希望将权重类型和 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 消息编译器为旧消息编译的,我能否将它们解析为新消息? 文档对此非常模糊:

"Move optional fields into or out of a oneof: You may lose some of your information (some fields will be cleared) after the message is serialized and parsed. However, you can safely move a single field into a new oneof and may be able to move multiple fields if it is known that only one is ever set."

有人试过这个吗?这种情况的最佳做法是什么?

据我所知,oneof 中的字段只是使用它们的标签号进行序列化。序列化数据不指示字段是否是 oneof 的一部分。这全部由序列化器和反序列化器处理。因此,只要标签号不冲突,就可以假定它可以双向工作,旧消息发送到新序列化程序,新消息发送到旧序列化程序。

您可以使用 online protobuf deserializer.

进行测试

验证: 该代码确实产生了相同的字节串。您将在下面找到我使用的消息定义和 python 代码。 python 代码将输出一个字节字符串,您可以复制并在 Marc 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 在设置原始字段时产生相同的字节字符串。只有当您设置新字段时,您才会得到不同的字符串。