版本的功能設定

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,這些 proto 可能會在執行階段產生未指定的行為 (例如 many:1 或 1:many 對應)。

適用於以下範圍: 檔案、訊息、列舉

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 中的行為: 除了群組預設為 DELIMITED 之外,皆為 LENGTH_PREFIXED

在 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 選項在版本中已移轉至的功能。

可用值

  • 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

此功能決定具有開放列舉類型的欄位是否應該像封閉列舉一樣運作。這允許版本在 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 版本中,可以在欄位上指定 ctypestring_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 檔案所做的變更,以使 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]