作者:张兵在江湖 | 来源:互联网 | 2023-02-09 19:41
我需要在protobuf(proto3语法)中指定带有可选字段的消息.就proto 2语法而言,我想表达的信息是这样的:
message Foo {
required int32 bar = 1;
optional int32 baz = 2;
}
根据我的理解,"可选"概念已从语法proto 3(以及所需概念)中删除.虽然不清楚替代方法 - 使用默认值来声明尚未从发送方指定字段,但如果默认值属于有效值域,则会留下歧义(例如考虑布尔类型).
那么,我应该如何对上面的消息进行编码?谢谢.
1> Kenton Varda..:
在proto3中,所有字段都是"可选的"(因为如果发送方未能设置它们,则不是错误).但是,字段不再是"可空的",因为没有办法区分显式设置为默认值的字段与根本未设置的字段之间的区别.
如果您需要一个"null"状态(并且没有可用于此的超出范围的值),那么您将需要将其编码为单独的字段.例如,你可以这样做:
message Foo {
bool has_baz = 1; // always set this to "true" when using baz
int32 baz = 2;
}
或者,您可以使用oneof
:
message Foo {
oneof baz {
bool baz_null = 1; // always set this to "true" when null
int32 baz_value = 2;
}
}
该oneof
版本在线路上更明确,更高效,但需要了解oneof
值如何工作.
最后,另一个完全合理的选择是坚持使用proto2.Proto2并没有被弃用,事实上很多项目(包括Google内部)都非常依赖proto2中删除的proto2功能,因此它们可能永远不会切换.因此,在可预见的将来继续使用它是安全的.
我找到了消息Foo {oneof baz {int32 baz_value = 1; 很好地工作.
@MaxP您的解决方案有效,但我建议在空消息上使用布尔值.要么将在线路上占用两个字节,但空消息将占用相当多的CPU,RAM和生成的代码膨胀来处理.
2> VM4..:
一种方法是使用oneof
在接受的答案中建议的方式.
另一个是使用包装器对象.你不需要像google已经提供的那样自己编写它们:
在.proto文件的顶部添加此导入:
import "google/protobuf/wrappers.proto";
现在,您可以为每种简单类型使用特殊包装:
DoubleValue
FloatValue
Int64Value
UInt64Value
Int32Value
UInt32Value
BoolValue
StringValue
BytesValue
所以要回答原始问题,这种包装的用法可能是这样的:
message Foo {
int32 bar = 1;
google.protobuf.Int32Value baz = 2;
}
现在,例如在Java中我可以做类似的事情:
if(foo.hasBaz()) { ... }
这是如何运作的?当`baz = null`且未通过`baz`时,两种情况下都有'hasBaz()`说成'false'!
3> CyberSnoopy..:
根据Kenton的回答,一个更简单但更有效的解决方案如下:
message Foo {
oneof optional_baz { // "optional_" prefix here just serves as an indicator, not keyword in proto2
int32 baz = 1;
}
}
基本上,其中一个名字很差.它意味着"至多一个".总有一个可能的空值.