版本的功能設定

Protobuf 版本功能以及它們如何影響 protobuf 行為。

本主題概述了版本 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;
}

請注意,requiredoptional 標籤在版本中已不再存在,因為對應的行為是使用 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 訊息編碼
  • 訊息類型與欄位定義在相同範圍中
  • 欄位名稱與小寫類型名稱完全相同

可用值

  • LENGTH_PREFIXED:欄位使用 訊息結構中描述的 LEN 連線類型進行編碼。
  • 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

此功能是版本中已遷移的 repeated 欄位之 proto2/proto3 packed 選項

可用值

  • PACKED:基本類型的 Repeated 欄位會編碼為包含每個串連元素的單一 LEN 記錄。
  • EXPANDEDRepeated 欄位會針對每個值使用欄位編號進行編碼。

適用於以下範圍: 檔案、欄位

版本 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

此功能決定具有開放式列舉類型的欄位是否應表現得像封閉式列舉。這讓版本能夠重現 proto2 和 proto3 中 Java 和 C++ 的不相符行為

此功能不會影響 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 中,可以在欄位上指定 ctypestring_type,但不能同時指定兩者。

可用值

  • 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;
}

features.(pb.java).large_enum

語言: Java

此語言特定功能可讓您採用可處理 Java 中大型列舉的新功能,而不會造成編譯器錯誤。

這是新行為,因此不會影響 proto2 或 proto3 結構描述定義檔。

可用值

  • true:Java 列舉將使用新功能。
  • false:Java 列舉將繼續使用 Java 列舉。

適用於以下範圍: 列舉

版本 2023 中的預設行為: false

proto2 中的行為: false

proto3 中的行為: false

保留 proto2 或 proto3 行為

您可能想要移至版本格式,但暫時不處理產生程式碼行為方式的更新。本節說明 Prototiller 工具對您的 .proto 檔案所做的變更,以使版本 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 欄位選項,並在欄位層級新增 [features.repeated_field_encoding=PACKED],當您不想要在Proto2 行為中設定的 EXPANDED 行為時。對於轉換為版本格式的 proto3 檔案,當您不想要預設的 proto3 行為時,請在欄位層級新增 [features.repeated_field_encoding=EXPANDED]