版本的功能设置
本主题概述了 Edition 2023 中包含的功能。后续每个版本的功能都将添加到本主题中。我们在新闻部分中宣布新版本。
在新的模式定义内容中配置功能设置之前,请确保您了解为什么要使用它们。避免在功能上盲目照搬。
Prototiller
Prototiller 是一个命令行工具,可将 proto2 和 proto3 定义文件转换为 Editions 语法。它尚未发布,但在本主题中被引用。
功能
以下部分包括 Edition 2023 中所有可使用功能配置的行为。保留 proto2 或 proto3 行为展示了如何覆盖默认行为,以便您的 proto 定义文件表现得像 proto2 或 proto3 文件。有关版本和功能如何协同工作以设置行为的更多信息,请参阅Protobuf 版本概述。
以下每个部分都有一个条目,说明该功能适用于哪个范围。这可以包括文件、枚举、消息或字段。以下示例显示了应用于每个范围的模拟功能
edition = "2023";
// File-scope definition
option features.bar = BAZ;
enum Foo {
// Enum-scope definition
option features.bar = QUX;
A = 1;
B = 2;
}
message Corge {
// Message-scope definition
option features.bar = QUUX;
// Field-scope definition
Foo A = 1 [features.bar = GRAULT];
}
在此示例中,字段范围功能定义中的设置 GRAULT
覆盖了消息范围 QUUX 设置。
features.enum_type
此功能设置了如何处理未包含在已定义集合中的枚举值的行为。有关开放枚举和封闭枚举的更多信息,请参阅枚举行为。
此功能不影响 proto3 文件,因此本节没有 proto3 文件的前后对比。
可用值
CLOSED:
封闭枚举将超出范围的枚举值存储在未知字段集中。OPEN:
开放枚举直接将超出范围的值解析到其字段中。
适用范围: 文件、枚举
Edition 2023 中的默认行为: OPEN
proto2 中的行为: CLOSED
proto3 中的行为: OPEN
以下代码示例显示了一个 proto2 文件
syntax = "proto2";
enum Foo {
A = 2;
B = 4;
C = 6;
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
enum Foo {
option features.enum_type = CLOSED;
A = 2;
B = 4;
C = 6;
}
features.field_presence
此功能设置了跟踪字段存在性的行为,即 protobuf 字段是否具有值的概念。
可用值
LEGACY_REQUIRED
:该字段是解析和序列化所必需的。任何显式设置的值都将被序列化到网络上(即使它与默认值相同)。EXPLICIT
:该字段具有显式的存在性跟踪。任何显式设置的值都将被序列化到网络上(即使它与默认值相同)。对于单数原始字段,将为设置为EXPLICIT
的字段生成has_*
函数。IMPLICIT
:该字段没有存在性跟踪。默认值不会被序列化到网络上(即使它是显式设置的)。不会为设置为IMPLICIT
的字段生成has_*
函数。
适用范围: 文件、字段
Edition 2023 中的默认值: EXPLICIT
proto2 中的行为: EXPLICIT
proto3 中的行为: IMPLICIT
,除非字段具有 optional
标签,在这种情况下,它的行为类似于 EXPLICIT
。有关更多信息,请参阅Proto3 API 中的存在性。
以下代码示例显示了一个 proto2 文件
syntax = "proto2";
message Foo {
required int32 x = 1;
optional int32 y = 2;
repeated int32 z = 3;
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
message Foo {
int32 x = 1 [features.field_presence = LEGACY_REQUIRED];
int32 y = 2;
repeated int32 z = 3;
}
以下显示了一个 proto3 文件
syntax = "proto3";
message Bar {
int32 x = 1;
optional int32 y = 2;
repeated int32 z = 3;
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
option features.field_presence = IMPLICIT;
message Bar {
int32 x = 1;
int32 y = 2 [features.field_presence = EXPLICIT];
repeated int32 z = 3;
}
请注意,required
和 optional
标签在 Editions 中不再存在,因为相应的行为是通过 field_presence
功能显式设置的。
features.json_format
此功能设置了 JSON 解析和序列化的行为。
此功能不影响 proto3 文件,因此本节没有 proto3 文件的前后对比。Editions 行为与 proto3 中的行为匹配。
可用值
ALLOW
:运行时必须允许 JSON 解析和序列化。在 proto 级别应用检查,以确保存在到 JSON 的明确定义的映射。LEGACY_BEST_EFFORT
:运行时尽力解析和序列化 JSON。允许某些 proto 可能会导致运行时出现未指定的行为(例如 many:1 或 1:many 映射)。
适用范围: 文件、消息、枚举
Edition 2023 中的默认行为: ALLOW
proto2 中的行为: LEGACY_BEST_EFFORT
proto3 中的行为: ALLOW
以下代码示例显示了一个 proto2 文件
syntax = "proto2";
message Foo {
// Warning only
string bar = 1;
string bar_ = 2;
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
features.json_format = LEGACY_BEST_EFFORT;
message Foo {
string bar = 1;
string bar_ = 2;
}
features.message_encoding
此功能设置了序列化时编码字段的行为。
此功能不影响 proto3 文件,因此本节没有 proto3 文件的前后对比。
根据语言的不同,“类似组”的字段在生成的代码和文本格式中可能具有一些意外的大写,以便提供与 proto2 的向后兼容性。如果满足以下所有条件,则消息字段是“类似组”的
- 已指定
DELIMITED
消息编码 - 消息类型与字段在同一范围内定义
- 字段名称与小写类型名称完全相同
可用值
适用范围: 文件、字段
Edition 2023 中的默认行为: LENGTH_PREFIXED
proto2 中的行为: LENGTH_PREFIXED
,但组除外,组默认为 DELIMITED
proto3 中的行为: LENGTH_PREFIXED
。Proto3 不支持 DELIMITED
。
以下代码示例显示了一个 proto2 文件
syntax = "proto2";
message Foo {
group Bar = 1 {
optional int32 x = 1;
repeated int32 y = 2;
}
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
message Foo {
message Bar {
int32 x = 1;
repeated int32 y = 2;
}
Bar bar = 1 [features.message_encoding = DELIMITED];
}
features.repeated_field_encoding
此功能是 proto2/proto3 packed
选项 for repeated
字段已在 Editions 中迁移到的功能。
可用值
PACKED
:原始类型的Repeated
字段编码为包含每个元素连接的单个 LEN 记录。EXPANDED
:Repeated
字段各自使用每个值的字段编号进行编码。
适用范围: 文件、字段
Edition 2023 中的默认行为: PACKED
proto2 中的行为: EXPANDED
proto3 中的行为: PACKED
以下代码示例显示了一个 proto2 文件
syntax = "proto2";
message Foo {
repeated int32 bar = 6 [packed=true];
repeated int32 baz = 7;
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
option features.repeated_field_encoding = EXPANDED;
message Foo {
repeated int32 bar = 6 [features.repeated_field_encoding=PACKED];
repeated int32 baz = 7;
}
以下显示了一个 proto3 文件
syntax = "proto3";
message Foo {
repeated int32 bar = 6;
repeated int32 baz = 7 [packed=false];
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
message Foo {
repeated int32 bar = 6;
repeated int32 baz = 7 [features.repeated_field_encoding=EXPANDED];
}
features.utf8_validation
此功能设置了字符串的验证方式。它适用于所有语言,除非有特定于语言的 utf8_validation
功能覆盖它。有关 Java 语言特定功能,请参阅features.(pb.java).utf8_validation
。
此功能不影响 proto3 文件,因此本节没有 proto3 文件的前后对比。
可用值
VERIFY
:运行时应验证 UTF-8。这是默认的 proto3 行为。NONE
:该字段的行为类似于网络上未验证的bytes
字段。解析器可能会以不可预测的方式处理这种类型的字段,例如替换无效字符。这是默认的 proto2 行为。
适用范围: 文件、字段
Edition 2023 中的默认行为: VERIFY
proto2 中的行为: NONE
proto3 中的行为: VERIFY
以下代码示例显示了一个 proto2 文件
syntax = "proto2";
message MyMessage {
string foo = 1;
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
message MyMessage {
string foo = 1 [features.utf8_validation = NONE];
}
特定于语言的功能
某些功能适用于特定语言,而不适用于其他语言中的相同 proto。使用这些功能需要您从语言的运行时导入相应的 *_features.proto 文件。以下部分中的示例显示了这些导入。
features.(pb.cpp/pb.java).legacy_closed_enum
语言: C++,Java
此功能确定具有开放枚举类型的字段是否应表现得像封闭枚举。这允许版本重现 proto2 和 proto3 中 Java 和 C++ 的不符合规范的行为。
此功能不影响 proto3 文件,因此本节没有 proto3 文件的前后对比。
可用值
true
:将枚举视为封闭枚举,而与enum_type
无关。false
:遵守在enum_type
中设置的任何内容。
适用范围: 文件、字段
Edition 2023 中的默认行为: false
proto2 中的行为: true
proto3 中的行为: false
以下代码示例显示了一个 proto2 文件
syntax = "proto2";
import "myproject/proto3file.proto";
message Msg {
myproject.proto3file.Proto3Enum name = 1;
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
import "myproject/proto3file.proto";
import "google/protobuf/cpp_features.proto";
import "google/protobuf/java_features.proto";
message Msg {
myproject.proto3file.Proto3Enum name = 1 [
features.(pb.cpp).legacy_closed_enum = true,
features.(pb.java).legacy_closed_enum = true
];
}
features.(pb.cpp).string_type
语言: C++
此功能确定生成的代码应如何处理字符串字段。这取代了 proto2 和 proto3 中的 ctype
选项,并提供了一个新的 string_view
功能。在 Edition 2023 中,可以在字段上指定 ctype
或 string_type
,但不能同时指定两者。
可用值
VIEW
:为字段生成string_view
访问器。这将是未来版本中的默认设置。CORD
:为字段生成Cord
访问器。STRING
:为字段生成string
访问器。
适用范围: 文件、字段
Edition 2023 中的默认行为: STRING
proto2 中的行为: STRING
proto3 中的行为: STRING
以下代码示例显示了一个 proto2 文件
syntax = "proto2";
message Foo {
optional string bar = 6;
optional string baz = 7 [ctype = CORD];
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
import "google/protobuf/cpp_features.proto";
message Foo {
string bar = 6;
string baz = 7 [features.(pb.cpp).string_type = CORD];
}
以下显示了一个 proto3 文件
syntax = "proto3"
message Foo {
string bar = 6;
string baz = 7 [ctype = CORD];
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
import "google/protobuf/cpp_features.proto";
message Foo {
string bar = 6;
string baz = 7 [features.(pb.cpp).string_type = CORD];
}
features.(pb.java).utf8_validation
语言: Java
此特定于语言的功能使您能够在字段级别覆盖 Java 的文件级别设置。
此功能不影响 proto3 文件,因此本节没有 proto3 文件的前后对比。
可用值
DEFAULT
:行为与features.utf8_validation
设置的行为相匹配。VERIFY
:覆盖文件级别features.utf8_validation
设置,强制仅对 Java 设置为VERIFY
。
适用范围: 字段、文件
Edition 2023 中的默认行为: DEFAULT
proto2 中的行为: DEFAULT
proto3 中的行为: DEFAULT
以下代码示例显示了一个 proto2 文件
syntax = "proto2";
option java_string_check_utf8=true;
message MyMessage {
string foo = 1;
string bar = 2;
}
运行 Prototiller 后,等效代码可能如下所示
edition = "2023";
import "google/protobuf/java_features.proto";
option features.utf8_validation = NONE;
option features.(pb.java).utf8_validation = VERIFY;
message MyMessage {
string foo = 1;
}
features.(pb.java).large_enum
语言: Java
此特定于语言的功能使您能够采用处理 Java 中大型枚举的新功能,而不会导致编译器错误。
这是新行为,因此不影响 proto2 或 proto3 模式定义文件。
可用值
true
:Java 枚举将使用新功能。false
:Java 枚举将继续使用 Java 枚举。
适用范围: 枚举
Edition 2023 中的默认行为: false
proto2 中的行为: false
proto3 中的行为: false
保留 proto2 或 proto3 行为
您可能希望迁移到版本格式,但暂不处理生成代码行为方式的更新。本节介绍 Prototiller 工具对您的 .proto 文件所做的更改,以使 Edition 2023 proto 的行为类似于 proto2 或 proto3 文件。
在文件级别进行这些更改后,您将获得 proto2 或 proto3 默认值。您可以在较低级别(消息级别、字段级别)覆盖,以考虑其他行为差异(例如required、proto3 optional)或者如果您希望您的定义仅大致类似于 proto2 或 proto3。
我们建议使用 Prototiller,除非您有不使用的特定原因。要手动应用所有这些而不是使用 Prototiller,请将以下部分的内容添加到您的 .proto 文件的顶部。
Proto2 行为
edition = "2023";
import "google/protobuf/cpp_features.proto";
import "google/protobuf/java_features.proto";
option features.field_presence = EXPLICIT;
option features.enum_type = CLOSED;
option features.repeated_field_encoding = EXPANDED;
option features.json_format = LEGACY_BEST_EFFORT;
option features.utf8_validation = NONE;
option features.(pb.cpp).legacy_closed_enum = true;
option features.(pb.java).legacy_closed_enum = true;
Proto3 行为
// proto3 behaviors
edition = "2023";
import "google/protobuf/cpp_features.proto";
import "google/protobuf/java_features.proto";
option features.field_presence = IMPLICIT;
option features.enum_type = OPEN;
// `packed=false` needs to be transformed to field-level repeated_field_encoding
// features in Editions syntax
option features.json_format = ALLOW;
option features.utf8_validation = VERIFY;
option features.(pb.cpp).legacy_closed_enum = false;
option features.(pb.java).legacy_closed_enum = false;
注意事项和例外
本节介绍如果您选择不使用 Prototiller,则需要手动进行的更改。
在大多数情况下,设置上一节中显示的文件级别默认值会设置默认行为,但也有一些例外。
optional
:删除所有optional
标签的实例,如果文件默认值为IMPLICIT
,则将features.field_presence
更改为EXPLICIT
。required
:删除所有required
标签的实例,并在字段级别添加features.field_presence=LEGACY_REQUIRED
选项。groups
:将groups
解包到单独的消息中,并在字段级别添加features.message_encoding = DELIMITED
选项。有关更多信息,请参阅features.message_encoding
。java_string_check_utf8
:删除此文件选项,并将其替换为features.(pb.java).utf8_validation
。您需要导入 Java 功能,如特定于语言的功能中所述。packed
:对于转换为版本格式的 proto2 文件,如果您不希望在Proto2 行为中设置的EXPANDED
行为,请删除packed
字段选项并在字段级别添加[features.repeated_field_encoding=PACKED]
。对于转换为版本格式的 proto3 文件,如果您不希望默认的 proto3 行为,请在字段级别添加[features.repeated_field_encoding=EXPANDED]
。