版本特性设置
本主题概述了版本 2023 中包含的特性。每个后续版本的特性都将添加到本主题中。我们在新闻版块中宣布新的版本。
Prototiller
Prototiller 是一种命令行工具,用于将 proto2 和 proto3 定义文件转换为版本语法。它尚未发布,但在本主题中有所提及。
特性
以下部分包含所有可以使用版本 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:
开放枚举将超出范围的值直接解析到其字段中。
适用于以下范围: 文件、枚举
版本 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_*
函数。
适用于以下范围: 文件、字段
版本 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
标签在版本中不再存在,因为相应行为是使用 field_presence
特性显式设置的。
features.json_format
此特性设置 JSON 解析和序列化的行为。
此特性不影响 proto3 文件,因此本部分没有 proto3 文件的前后对比。版本行为与 proto3 中的行为一致。
可用值
ALLOW
:运行时必须允许 JSON 解析和序列化。在 proto 级别应用检查以确保存在到 JSON 的明确映射。LEGACY_BEST_EFFORT
:运行时尽其所能解析和序列化 JSON。允许某些 proto 可能会导致运行时出现未指定的行为(例如多对一或一对多映射)。
适用于以下范围: 文件、消息、枚举
版本 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
消息编码 - 消息类型在与字段相同的范围内定义
- 字段名称与小写类型名称完全相同
可用值
适用于以下范围: 文件、字段
版本 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/proto3packed
选项在版本中迁移到的位置,用于 repeated
字段。
可用值
PACKED
:原始类型的Repeated
字段编码为单个 LEN 记录,其中包含每个元素的串联。EXPANDED
:每个Repeated
字段都使用每个值的字段编号进行编码。
适用于以下范围: 文件、字段
版本 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 的默认行为。
适用于以下范围: 文件、字段
版本 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
此特性确定具有开放枚举类型的字段是否应表现得好像它是封闭枚举。这允许版本在 Java 和 C++ 中重现 proto2 和 proto3 中的不符合规范的行为。
此特性不影响 proto3 文件,因此本部分没有 proto3 文件的前后对比。
可用值
true
:无论enum_type
如何,都将枚举视为封闭的。false
:尊重enum_type
中设置的内容。
适用于以下范围: 文件、字段
版本 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
特性。在版本 2023 中,可以在字段上指定 ctype
或 string_view
,但不能同时指定两者。
可用值
VIEW
:为字段生成string_view
访问器。这将在未来的版本中成为默认值。CORD
:为字段生成Cord
访问器。STRING
:为字段生成string
访问器。
适用于以下范围: 文件、字段
版本 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
。
适用于以下范围: 字段、文件
版本 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;
}
保留 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 文件,删除packed
字段选项,并在您不希望使用 Proto2 行为 中设置的EXPANDED
行为时,在字段级别添加[features.repeated_field_encoding=PACKED]
。对于转换为版本格式的 proto3 文件,在您不希望使用默认的 proto3 行为时,在字段级别添加[features.repeated_field_encoding=EXPANDED]
。