要在.proto中定义oneof,请使用oneof关键字,后跟您的oneof名称,在本例中为test_oneof:
message SampleMessage { oneof test_oneof { string name = 4; SubMessage sub_message = 9; } }然后,将oneof字段添加到oneof定义。 您可以添加任何类型的字段,但地图字段和重复字段除外。
在生成的代码中,oneof字段具有与常规字段相同的getter和setter。 您还将获得一种特殊的方法来检查oneof中的哪个值(如果有)。 您可以在相关的API参考中找到有关所选语言的oneof API的更多信息。
Oneof Features设置oneof字段将自动清除oneof的所有其他成员。 因此,如果您设置了多个字段,则只有您设置的最后一个字段仍具有值。
SampleMessage message; message.set_name("name"); CHECK(message.has_name()); message.mutable_sub_message(); // Will clear name field. CHECK(!message.has_name());
如果解析器在线路上遇到同一个对象的多个成员,则在解析的消息中仅使用最后看到的成员。
一个不能重复。
反射API适用于其中一个字段。
如果将oneof字段设置为默认值(例如将int32 oneof字段设置为0),则将设置该oneof字段的“大小写”,并且该值将在线路上序列化。
如果您使用的是C++,请确保您的代码不会导致内存崩溃。 以下示例代码将崩溃,因为通过调用set_name()方法已经删除了sub_message。
SampleMessage message; SubMessage* sub_message = message.mutable_sub_message(); message.set_name("name"); // Will delete sub_message sub_message->set_... // Crashes here同样,在C ++中,如果您用aofs交换(两条)消息,则每条消息都将以另一种形式的oneof结尾:在下面的示例中,msg1将具有sub_message,而msg2将具有名称。
SampleMessage msg1; msg1.set_name("name"); SampleMessage msg2; msg2.mutable_sub_message(); msg1.swap(&msg2); CHECK(msg1.has_sub_message()); CHECK(msg2.has_name()); Backwards-compatibility issues添加或删除字段之一时请多加注意。 如果检查oneof的值返回None / NOT_SET,则可能表示尚未设置oneof或已将其设置为oneof的不同版本中的字段。 由于无法知道导线上的未知字段是否是oneof的成员,因此无法分辨出差异。
Maps如果要在数据定义中创建关联映射,则协议缓冲区提供了方便的快捷方式语法:
map<key_type, value_type> map_field = N;...其中key_type可以是任何整数或字符串类型(因此,浮点类型和字节除外的任何标量类型)。 请注意,枚举不是有效的key_type。 value_type可以是除另一个映射以外的任何类型。
因此,例如,如果您想创建一个项目地图,其中每个Project消息都与一个字符串键相关联,则可以这样定义它:
map<string, Project> projects = 3;映射字段不能重复。
地图值的线格式排序和地图迭代排序是不确定的,因此您不能依赖于地图项的特定顺序。
为.proto生成文本格式时,地图按键排序。 数字键按数字排序。
从导线解析或合并时,如果存在重复的映射键,则使用最后看到的键。 从文本格式解析地图时,如果键重复,则解析可能会失败。
如果为映射字段提供键但没有值,则序列化字段时的行为取决于语言。 在C ++,Java和Python中,类型的默认值是序列化的,而在其他语言中,则没有序列化的值。
生成的地图API当前可用于所有proto3支持的语言。 您可以在相关API参考中找到有关所选语言的map API的更多信息。
Backwards compatibility映射语法与网上的以下语法等效,因此不支持映射的协议缓冲区实现仍可以处理您的数据:
message MapFieldEntry { key_type key = 1; value_type value = 2; } repeated MapFieldEntry map_field = N;任何支持映射的协议缓冲区实现都必须产生并接受上述定义可以接受的数据。
Packages您可以在.proto文件中添加可选的包说明符,以防止协议消息类型之间的名称冲突。
package foo.bar; message Open { ... }然后,您可以在定义消息类型的字段时使用包说明符:
message Foo { ... foo.bar.Open open = 1; ... }包说明符影响生成的代码的方式取决于您选择的语言:
在C ++中,生成的类包装在C ++名称空间中。例如,Open将位于名称空间foo :: bar中。
在Java中,除非您在.proto文件中明确提供选项java_package,否则该包将用作Java包。
在Python中,package指令将被忽略,因为Python模块是根据其在文件系统中的位置进行组织的。
在Go中,除非您在.proto文件中明确提供了go_package选项,否则该包将用作Go包名称。
在Ruby中,生成的类被包装在嵌套的Ruby名称空间中,转换为所需的Ruby大写样式(首字母大写;如果首字符不是字母,则以PB_开头)。例如,Open将位于命名空间Foo :: Bar中。