如何在protobuf 3中定义可选字段

Max*_*axP 61 protocol-buffers protocol-buffers-3

我需要在protobuf(proto3语法)中指定带有可选字段的消息.就proto 2语法而言,我想表达的信息是这样的:

message Foo {
    required int32 bar = 1;
    optional int32 baz = 2;
}
Run Code Online (Sandbox Code Playgroud)

根据我的理解,"可选"概念已从语法proto 3(以及所需概念)中删除.虽然不清楚替代方法 - 使用默认值来声明尚未从发送方指定字段,但如果默认值属于有效值域,则会留下歧义(例如考虑布尔类型).

那么,我应该如何对上面的消息进行编码?谢谢.

jar*_*obs 99

从 protobuf版本 3.15 开始,proto3 支持使用optional关键字(就像在 proto2 中一样)来提供标量字段存在信息。

syntax = "proto3";

message Foo {
    int32 bar = 1;
    optional int32 baz = 2;
}
Run Code Online (Sandbox Code Playgroud)

为上面的字段生成了一个has_baz()/hasBaz()方法optional,就像在 proto2 中一样。

幕后protoc有效地将optional字段视为使用oneof包装器声明的字段,正如Cyber​​Snoopy 的回答所暗示的那样:

message Foo {
    int32 bar = 1;
    oneof optional_baz {
        int32 baz = 2;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您已经使用过这种方法,那么您现在可以简化您的消息声明(从 切换oneofoptional)和代码,因为有线格式是相同的。

有关现场存在和optionalproto3 的具体细节可以在应用说明中找到:现场存在文档。

历史记录:optional2020 年 4 月 23 日在此评论中首次宣布了对 in proto3 的实验性支持。使用它需要--experimental_allow_proto3_optional在 3.12-3.14 版本中传递 protoc标志。

  • 你知道如何在 C# 中传递标志吗? (5认同)
  • @JónásBalázs 将 --experimental_allow_proto3_optional 标志传递给 protoc 以在版本 3.12 中使用此功能。 (2认同)

Ken*_*rda 80

在proto3中,所有字段都是"可选的"(因为如果发送方未能设置它们,则不是错误).但是,字段不再是"可空的",因为没有办法区分显式设置为默认值的字段与根本未设置的字段之间的区别.

如果您需要一个"null"状态(并且没有可用于此的超出范围的值),那么您将需要将其编码为单独的字段.例如,你可以这样做:

message Foo {
  bool has_baz = 1;  // always set this to "true" when using baz
  int32 baz = 2;
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用oneof:

message Foo {
  oneof baz {
    bool baz_null = 1;  // always set this to "true" when null
    int32 baz_value = 2;
  }
}
Run Code Online (Sandbox Code Playgroud)

oneof版本在线路上更明确,更高效,但需要了解oneof值如何工作.

最后,另一个完全合理的选择是坚持使用proto2.Proto2并没有被弃用,事实上很多项目(包括Google内部)都非常依赖proto2中删除的proto2功能,因此它们可能永远不会切换.因此,在可预见的将来继续使用它是安全的.

  • 我找到了消息Foo {oneof baz {int32 baz_value = 1; 很好地工作. (6认同)
  • @MaxP您的解决方案有效,但我建议在空消息上使用布尔值.要么将在线路上占用两个字节,但空消息将占用相当多的CPU,RAM和生成的代码膨胀来处理. (2认同)

VM4*_*VM4 57

一种方法是使用oneof在接受的答案中建议的方式.

另一个是使用包装器对象.你不需要像google已经提供的那样自己编写它们:

在.proto文件的顶部添加此导​​入:

import "google/protobuf/wrappers.proto";

现在,您可以为每种简单类型使用特殊包装:

DoubleValue
FloatValue
Int64Value
UInt64Value
Int32Value
UInt32Value
BoolValue
StringValue
BytesValue
Run Code Online (Sandbox Code Playgroud)

所以要回答原始问题,这种包装的用法可能是这样的:

message Foo {
    int32 bar = 1;
    google.protobuf.Int32Value baz = 2;
}
Run Code Online (Sandbox Code Playgroud)

现在,例如在Java中我可以做类似的事情:

if(foo.hasBaz()) { ... }

  • 比原来的awsner更好! (6认同)
  • 这是如何运作的?当`baz = null`且未通过`baz`时,两种情况下都有'hasBaz()`说成'false'! (3认同)
  • 这是要走的路。使用 C#,生成的代码会生成 Nullable<T> 属性。 (3认同)

Cyb*_*opy 18

根据Kenton的回答,一个更简单但更有效的解决方案如下:

message Foo {
    oneof optional_baz { // "optional_" prefix here just serves as an indicator, not keyword in proto2
        int32 baz = 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 基本上,其中一个名字很差.它意味着"至多一个".总有一个可能的空值. (11认同)

Ben*_*ert 7

此处扩展@cybersnoopy 的建议

如果您有一个带有如下消息的 .proto 文件:

message Request {
    oneof option {
        int64 option_value = 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用提供的案例选项(java 生成的代码)

所以我们现在可以写一些代码如下:

Request.OptionCase optionCase = request.getOptionCase();
OptionCase optionNotSet = OPTION_NOT_SET;

if (optionNotSet.equals(optionCase)){
    // value not set
} else {
    // value set
}
Run Code Online (Sandbox Code Playgroud)


luk*_*yer 7

只需使用:

syntax = "proto3";

message Hello {
    int64 required_id = 1;
    optional int64 optional_id = 2;
}
Run Code Online (Sandbox Code Playgroud)

在 Go 中,它构建结构体

type Hello struct {
   ...
   RequiredId int64 ...
   OptionalId *int64 ...
   ...
}
Run Code Online (Sandbox Code Playgroud)

您可以轻松检查nil并区分默认值(零)和未设置值(nil)。

这里的大多数答案都已经过时并且不必要地复杂。