How to define an optional field in protobuf 3
我需要在protobuf(proto3语法)中指定一个带有可选字段的消息。 用proto 2语法来说,我要表达的信息是这样的:
1 2 3 4 | message Foo { required int32 bar = 1; optional int32 baz = 2; } |
据我了解,"可选"概念已从语法原型3中删除(以及必需的概念)。 尽管尚不清楚替代方案-使用默认值声明尚未从发送方指定字段,但如果默认值属于有效值域(例如,考虑布尔类型),则仍会造成歧义。
那么,我应该如何编码上面的消息? 谢谢。
在proto3中,所有字段均为"可选"(如果发件人未能设置它们,这不是错误)。但是,字段不再是"可为空的",因为无法分辨出明确设置为默认值的字段与根本没有设置默认值之间的区别。
如果需要"空"状态(并且没有可用于此的超出范围的值),则需要将其编码为单独的字段。例如,您可以执行以下操作:
1 2 3 4 | message Foo { bool has_baz = 1; // always set this to"true" when using baz int32 baz = 2; } |
另外,您可以使用
1 2 3 4 5 6 | message Foo { oneof baz { bool baz_null = 1; // always set this to"true" when null int32 baz_value = 2; } } |
最后,另一个完全合理的选择是坚持使用proto2。 Proto2并没有被弃用,事实上,许多项目(包括Google内部)都非常依赖proto2的功能,这些功能已在proto3中删除,因此它们可能永远不会切换。因此,在可预见的将来继续使用它是安全的。
一种方法是使用
另一个是使用包装对象。您不需要自己编写它们,因为Google已经提供了它们:
在您的.proto文件顶部,添加以下导入:
现在,您可以对每种简单类型使用特殊包装器:
1 2 3 4 5 6 7 8 9 | DoubleValue FloatValue Int64Value UInt64Value Int32Value UInt32Value BoolValue StringValue BytesValue |
因此,要回答原始问题,此类包装器的用法可能是这样的:
1 2 3 4 | message Foo { int32 bar = 1; google.protobuf.Int32Value baz = 2; } |
现在以Java为例,我可以做类似的事情:
根据Kenton的回答,一个更简单但可行的解决方案如下所示:
1 2 3 4 5 | message Foo { oneof optional_baz { //"optional_" prefix here just serves as an indicator, not keyword in proto2 int32 baz = 1; } } |
从protobuf 3.12版开始,proto3支持使用
1 2 3 4 5 6 | syntax ="proto3"; message Foo { int32 bar = 1; optional int32 baz = 2; } |
就像在proto2中一样,上面的
正如Cyber??Snoopy的答案所暗示的那样,protoc可以有效地将
您可以在应用说明:字段存在文档中的proto3中找到有关字段存在和
在版本3.12中,此功能需要将
在这里扩展@cybersnoopy的建议
如果您有一个带有如下消息的.proto文件:
1 2 3 4 5 | message Request { oneof option { int64 option_value = 1; } } |
您可以使用提供的案例选项(java生成的代码):
因此,我们现在可以编写一些代码,如下所示:
1 2 3 4 5 6 7 8 | Request.OptionCase optionCase = request.getOptionCase(); OptionCase optionNotSet = OPTION_NOT_SET; if (optionNotSet.equals(optionCase)){ // value not set } else { // value set } |
关于此有一个很好的帖子:https://itnext.io/protobuf-and-null-support-1908a15311b6
解决方案取决于您的实际用例:
-
处理部分更新
-
支持空
另一种方法是可以对每个可选字段使用位掩码。并设置这些位(如果设置了值)并重置那些未设置值的位
1 2 3 4 5 6 7 8 9 10 11 | enum bitsV { baz_present = 1; // 0x01 baz1_present = 2; // 0x02 } message Foo { uint32 bitMask; required int32 bar = 1; optional int32 baz = 2; optional int32 baz1 = 3; } |
解析时检查bitMask的值。
1 2 3 4 5 | if (bitMask & baz_present) baz is present if (bitMask & baz1_present) baz1 is present |
您可以通过将引用与默认实例进行比较来查找是否已初始化:
1 2 3 4 | GRPCContainer container = myGrpcResponseBean.getContainer(); if (container.getDefaultInstanceForType() != container) { ... } |