注: このサイトは非推奨になりました。このサイトは 2023 年 1 月 31 日を過ぎると停止され、トラフィックは https://protobuf.dev の新しいサイトにリダイレクトされます。その間、更新は protobuf.dev のみに行われます。

言語ガイド(proto3)

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

このガイドでは、プロトコル バッファ言語を使用して .proto ファイル構文などのプロトコル バッファ データを構造化する方法と、.proto ファイルからデータアクセス クラスを生成する方法について説明します。プロトコル バッファ言語の proto3 バージョンについて説明します。proto2 の構文については、Proto2 言語ガイドをご覧ください。

リファレンス ガイドでは、このドキュメントで説明されている機能の多くを使用する詳細なステップ例については、選択した言語に関するチュートリアルをご覧ください(現在は proto2 のみ。その他の proto3 ドキュメントについては近日公開予定です)。

メッセージ タイプを定義する

まず、非常にシンプルな例を見てみましょう。検索リクエスト メッセージの形式を定義するとします。各検索リクエストには、クエリ文字列、関心のある結果のページ、ページあたりの結果の数が含まれます。メッセージ タイプの定義に使用する .proto ファイルは次のとおりです。

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
  • ファイルの最初の行には、proto3 構文を使用することを指定します。これを行わないと、プロトコル バッファ コンパイラは proto2 を使用していると見なされます。ファイルの最初の空でないコメント行である必要があります。
  • SearchRequest メッセージの定義では、3 つのフィールド(名前と値のペア)を指定します。このタイプのメッセージに含めるデータごとに 1 つずつあります。各フィールドには名前と型があります。

フィールド タイプの指定

上記の例では、すべてのフィールドがスカラー型(2 つの整数(page_numberresult_per_page)と文字列(query))です。ただし、列挙型やその他のメッセージ型など、フィールドの複合型を指定することもできます。

フィールド番号の割り当て

このように、メッセージ定義の各フィールドには一意の番号があります。 これらのフィールド番号は、メッセージ バイナリ形式でフィールドを識別するために使用されます。メッセージ タイプの使用後に変更しないでください。1 から 15 の範囲の数値は、フィールド番号とフィールドの型を含めて、エンコードに 1 バイトかかります(詳細については、プロトコル バッファ エンコードをご覧ください)。16 ~ 2047 のフィールド番号は 2 バイトです。したがって、頻繁に発生するメッセージ要素には 1 ~ 15 の数値を予約する必要があります。今後追加される可能性がある、頻繁に発生する要素を表示する余地を残してください。

指定できる最小のフィールド番号は 1、最大値は 229 - 1 または 536,870,911 です。また、19000 ~ 19999(FieldDescriptor::kFirstReservedNumberFieldDescriptor::kLastReservedNumber)はプロトコル バッファの実装のために予約されているため、使用できません。.proto でこれらの予約済み番号のいずれかを使用すると、プロトコル バッファ コンパイラはエラーを出します。同様に、予約済みのフィールド番号は使用できません。

フィールド ルールの指定

メッセージ フィールドは次のいずれかになります。

  • singular: 整形式のメッセージでは、このフィールドを 0 または 1 つ指定できます(複数指定することはできません)。proto3 構文を使用する場合、これは特定のフィールドに他のフィールド ルールが指定されていない場合のデフォルトのフィールド ルールです。ワイヤから解析されたかどうかを判断することはできません。デフォルト値でない場合、ワイヤにシリアル化されます。このテーマの詳細については、フィールドのプレゼンスをご覧ください。
  • optional: singular と同じですが、値が明示的に設定されているかどうかを確認できます。optional フィールドは、次の 2 つの状態のいずれかになります。
    • フィールドが設定されていて、ワイヤから明示的に設定または解析された値が含まれています。ケーブルにシリアル化されます。
    • フィールドは設定されておらず、デフォルト値を返します。ケーブルに直列化されません。
  • repeated: このフィールド形式は、正しい形式のメッセージで 0 回以上繰り返すことができます。繰り返し値の順序は保持されます。
  • map: Key-Value フィールドのペアです。このフィールド タイプの詳細については、マップをご覧ください。

proto3 では、スカラー数値型の repeated フィールドがデフォルトで packed エンコードが使用されます。packed エンコードの詳細については、プロトコル バッファ エンコードをご覧ください。

メッセージ タイプの追加

1 つの .proto ファイルで複数のメッセージ タイプを定義できます。これは、複数の関連メッセージを定義する場合に便利です。たとえば、SearchResponse メッセージ タイプに対応する返信メッセージ形式を定義する場合は、同じ .proto に追加できます。

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

message SearchResponse {
 ...
}

コメントを追加する

.proto ファイルにコメントを追加するには、C/C++ スタイルの // および /* ... */ 構文を使用します。

/* SearchRequest represents a search query, with pagination options to
 * indicate which results to include in the response. */

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // Which page number do we want?
  int32 result_per_page = 3;  // Number of results to return per page.
}

予約済みフィールド

フィールドを完全に削除するか、コメントアウトしてメッセージ型を更新した場合、将来のユーザーは、型を独自に更新するときにフィールド番号を再利用できます。これにより、後でデータの破損やプライバシー バグなど、同じ.protoの古いバージョンを読み込むと、重大な問題が発生することがあります。これを回避する方法の一つとして、削除されたフィールドのフィールド番号(および、JSON シリアル化で問題を引き起こす可能性がある名前)を指定します。reserved今後のユーザーがこれらのフィールド識別子を使用しようとすると、プロトコル バッファ コンパイラはエラーになります。

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
}

同じ reserved ステートメント内でフィールド名とフィールド番号を混在させることはできません。

.proto から生成される内容

.protoプロトコル バッファ コンパイラを実行すると、コンパイラは、記述されたメッセージ タイプに対応するコード(フィールド値の取得と設定、出力ストリームへのメッセージのシリアル化、入力ストリームからのメッセージの解析など)を、選択した言語で生成します。

  • C++ の場合、コンパイラは各 .proto から .h ファイルと .cc ファイルを生成し、ファイル内に記述されているメッセージ タイプごとにクラスを作成します。
  • Java の場合、コンパイラはメッセージ タイプごとにクラスを含む .java ファイルと、メッセージ クラス インスタンスを作成するための特別な Builder クラスを生成します。
  • Kotlin では、Java で生成されるコードに加えて、メッセージ タイプごとに .kt ファイルが生成されます。このファイルには、メッセージ インスタンスの作成を簡素化する DSL が含まれています。
  • Python は若干異なります。Python コンパイラは、.proto 内の各メッセージ タイプの静的記述子を使用してモジュールを生成します。このモジュールは、メタクラスで使用されるため、実行時に必要な Python データ アクセス クラスを作成します。
  • Go の場合、コンパイラはファイル内のメッセージ タイプごとに .pb.go ファイルを生成します。
  • Ruby の場合、コンパイラはメッセージ タイプを含む Ruby モジュールを含む .rb ファイルを生成します。
  • Objective-C の場合、コンパイラは各 .proto から pbobjc.h ファイルと pbobjc.m ファイルを生成し、ファイル内に記述されているメッセージ タイプごとにクラスを作成します。
  • C# の場合、コンパイラは各 .proto から .cs ファイルを生成し、ファイル内に記述されているメッセージ タイプごとにクラスを作成します。
  • Dart の場合、コンパイラはファイル内のメッセージ タイプごとにクラスを含む .pb.dart ファイルを生成します。

各言語の API の使用方法については、選択した言語(proto3 バージョンについては近日提供予定)のチュートリアルをご覧ください。API の詳細については、関連する API リファレンス(proto3 バージョンも近日提供予定)をご覧ください。

スカラー値型

スカラー メッセージ フィールドには、次のいずれかの型を指定できます。表には、.proto ファイルで指定した型と、自動的に生成されるクラスの対応する型が示されています。

.proto タイプ 備考 C++ 型 Java/Kotlin 型[1] Python 型[3] 移動のタイプ Ruby タイプ C# 型 PHP のタイプ Dart のタイプ
倍精度 double 倍精度 float float64 フローティング 倍精度 float 倍精度
float float float float 浮動小数点 32 フローティング float float 倍精度
int32 可変長エンコードを使用します。負の値のエンコードは非効率的 – フィールドに負の値が含まれる可能性がある場合は、代わりに sint32 を使用します。 int32 int int int32 Fixnum または Bignum(必要に応じて) int 整数 int
int64 可変長エンコードを使用します。負の数値をエンコードする効率が悪い - フィールドに負の値が含まれる可能性がある場合は、代わりに sint64 を使用します。 int64 long 整数/long[4] int64 ビッグナム long 整数 / 文字列[6] Int64
uint32 可変長エンコードを使用します。 uint32 整数[2] 整数/long[4] uint32 Fixnum または Bignum(必要に応じて) uint 整数 int
uint64 可変長エンコードを使用します。 uint64 長め[2] 整数 / long[4] uint64 ビッグナム ulong 整数/文字列[6] Int64
Sint32 可変長エンコードを使用します。符号付き整数値。これらは、通常の int32 よりも効率的に負の数をエンコードします。 int32 int int int32 Fixnum または Bignum(必要に応じて) int 整数 int
Sint64 可変長エンコードを使用します。符号付き整数値。これらは、通常の int64 よりも効率的に負の数をエンコードします。 int64 long 整数/long[4] int64 ビッグナム long 整数/文字列[6] Int64
固定 32 常に 4 バイトです。値が 228 より大きい場合、uint32 よりも効率的です。 uint32 整数[2] 整数/long[4] uint32 Fixnum または Bignum(必要に応じて) uint 整数 int
固定 64 常に 8 バイトです。値が 256 より大きい場合、uint64 よりも効率的です。 uint64 長め[2] 整数 / long[4] uint64 ビッグナム ulong 整数/文字列[6] Int64
修正済み 32 常に 4 バイトです。 int32 int int int32 Fixnum または Bignum(必要に応じて) int 整数 int
修正済み 64 常に 8 バイトです。 int64 long 整数/long[4] int64 ビッグナム long 整数/文字列[6] Int64
ブール値 ブール値 boolean ブール値 ブール値 TrueClass/FalseClass ブール値 boolean ブール値
文字列 文字列には、必ず UTF-8 エンコードまたは 7 ビット ASCII テキストを含める必要があり、232 以下にする必要があります。 文字列 String str/unicode[5] 文字列 文字列(UTF-8) 文字列 文字列 String
バイト 232 文字以下の任意のバイト シーケンスを含めることができます。 文字列 ByteString str(Python 2)
バイト(Python 3)
[]バイト 文字列(ASCII-8BIT) ByteString 文字列 List

メッセージをシリアル化するときにこれらの型がエンコードされる方法について詳しくは、プロトコル バッファ エンコードをご覧ください。

[1] Kotlin は、Java と Kotlin の混在するコードベースの互換性を確保するために、Java の対応する型(符号なし型も)を使用します。

[2] Java では、32 ビットと 64 ビットの符号なし整数は符号付きの符号を使用して表され、最上位のビットは単に符号ビットに格納されます。

[3] いずれの場合も、フィールドに値を設定すると、型が有効かどうかチェックされます。

[4] 64 ビットまたは符号なし 32 ビットの整数は、デコード時は常に長い整数として表現されますが、フィールドを設定するときに int が指定された場合、int 型にすることができます。いずれの場合も、値は設定時に表される型に収まる必要があります。[2] をご覧ください。

[5] Python 文字列はデコード時に Unicode として表されますが、ASCII 文字列を指定すると str にすることができます(変更される可能性があります)。

[6] 整数は 64 ビットマシンで使用され、文字列は 32 ビットマシンで使用されます。

デフォルト値

メッセージが解析されるときに、エンコードされたメッセージに特定の単一要素が含まれていない場合、解析されたオブジェクトの対応するフィールドはそのフィールドのデフォルト値に設定されます。これらのデフォルトはタイプ固有です。

  • 文字列の場合、デフォルト値は空の文字列です。
  • バイトの場合、デフォルト値は空のバイトです。
  • ブール値の場合、デフォルト値は false です。
  • 数値型の場合、デフォルト値は 0 です。
  • 列挙型のデフォルト値は最初に定義された列挙値で、0 にする必要があります。
  • メッセージ フィールドの場合、このフィールドが設定されていません。正確な値は言語によって異なります。詳しくは、生成されたコードに関するガイドをご覧ください。

繰り返しフィールドのデフォルト値は空です(通常、適切な言語の空のリストです)。

スカラー メッセージ フィールドの場合、メッセージが解析された後、フィールドが明示的に設定されているかどうか(ブール値が false に設定されているなど)や、まったく設定されていないかどうかを知ることはできません。メッセージ タイプを定義するときは、この点に注意してください。たとえば、false に設定するとデフォルトでこの動作が発生しないようにする場合、一部の動作に切り替えるブール値を設定しないでください。また、スカラー メッセージ フィールドがデフォルトに設定されている場合、値はネットワーク上でシリアル化されません。

生成されたコードにおけるデフォルトの動作の詳細については、選択した言語に関する生成コードガイドをご覧ください。

列挙型

メッセージ タイプを定義するときに、そのフィールドのいずれかで、事前定義された値のリストのいずれか 1 つのみが含まれるようにできます。たとえば、SearchRequest ごとに corpus フィールドを追加するとします。この場合、コーパスは UNIVERSALWEBIMAGESLOCALNEWSPRODUCTSVIDEO のいずれかです。これを行うには、取り得る値ごとに定数を指定して、メッセージ定義に enum を追加します。

次の例では、考えられるすべての値と Corpus 型のフィールドを持つ Corpus という enum を追加しています。

enum Corpus {
  CORPUS_UNSPECIFIED = 0;
  CORPUS_UNIVERSAL = 1;
  CORPUS_WEB = 2;
  CORPUS_IMAGES = 3;
  CORPUS_LOCAL = 4;
  CORPUS_NEWS = 5;
  CORPUS_PRODUCTS = 6;
  CORPUS_VIDEO = 7;
}
message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  Corpus corpus = 4;
}

ご覧のとおり、Corpus 列挙型の最初の定数は 0 にマッピングされます。すべての列挙型の定義には、最初の要素としてゼロにマッピングされる定数を含める必要があります。 その理由は次のとおりです。

  • 0 を数値のデフォルト値として使用できるようにするため、ゼロ値が必要です。
  • proto2 セマンティクスとの互換性を維持するために、ゼロ値は最初の要素でなければなりません。最初の列挙値は常にデフォルトです。

エイリアスを定義するには、同じ値を異なる列挙型定数に割り当てます。これを行うには、allow_alias オプションを true に設定する必要があります。そうでない場合、エイリアスが見つかったときにプロトコル コンパイラでエラー メッセージが生成されます。シリアル化解除中はエイリアス値がすべて有効ですが、シリアル化時には最初の値が常に使用されます。

enum EnumAllowingAlias {
  option allow_alias = true;
  EAA_UNSPECIFIED = 0;
  EAA_STARTED = 1;
  EAA_RUNNING = 1;
  EAA_FINISHED = 2;
}
enum EnumNotAllowingAlias {
  ENAA_UNSPECIFIED = 0;
  ENAA_STARTED = 1;
  // ENAA_RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.
  ENAA_FINISHED = 2;
}

列挙定数は 32 ビット整数の範囲内になければなりません。enum 値はこの接続で変数エンコードを使用するため、負の値は非効率的で、推奨されません。enum は、上記の例のようにメッセージ定義内または外部で定義できます。これらの enum は、.proto ファイル内の任意のメッセージ定義で再利用できます。また、_MessageType_._EnumType_ 構文を使用して、あるメッセージで宣言されている enum 型を別のメッセージ内のフィールドの型として使用することもできます。

enum を使用する .proto でプロトコル バッファ コンパイラを実行すると、生成されたコードには、対応する Java、Kotlin、または C++ の enum、またはランタイムで生成されるクラスの整数値を持つシンボリック定数のセットを作成するために使用される特別な EnumDescriptor クラスが含まれます。

シリアル化解除中、認識されない列挙値はメッセージに保持されます。ただし、シリアル化解除されたメッセージの表現方法は言語によって異なります。C++ や Go など、指定されたシンボルの範囲外の値を持つ開いている列挙型をサポートする言語では、不明な列挙値は基になる整数表現として保存されます。Java などの閉じられた列挙型を持つ言語では、列挙型のケースが認識されない値を表すために使用され、基盤となる整数に特別なアクセサでアクセスできます。いずれの場合も、メッセージがシリアル化されている場合でも、認識されない値はメッセージとシリアル化されます。

アプリでメッセージ enum を操作する方法の詳細については、選択した言語の生成コードガイドをご覧ください。

予約済みの値

列挙型エントリを完全に削除するかコメントアウトすることで列挙型を更新した場合、将来のユーザーは、型に独自の更新を行う際に数値を再利用できます。これにより、後でデータの破損やプライバシー バグなど、同じ.protoの古いバージョンを読み込むと、重大な問題が発生することがあります。これを回避する方法の一つは、削除されたエントリの数値(および JSON シリアル化の問題を引き起こす可能性がある名前)を reserved に指定することです。今後のユーザーがこれらの識別子を使用しようとすると、プロトコル バッファ コンパイラはエラーになります。max キーワードを使用して、予約された数値の範囲を最大値として指定できます。

enum Foo {
  reserved 2, 15, 9 to 11, 40 to max;
  reserved "FOO", "BAR";
}

同じ reserved ステートメント内でフィールド名と数値を組み合わせることはできません。

他のメッセージ タイプの使用

他のメッセージ タイプをフィールド タイプとして使用できます。たとえば、各 SearchResponse メッセージに Result メッセージを含める場合、同じ .protoResult メッセージ タイプを定義し、SearchResponseResult 型のフィールドを指定します。

message SearchResponse {
  repeated Result results = 1;
}

message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3;
}

定義のインポート

上記の例では、Result メッセージ タイプは SearchResponse と同じファイルで定義されています。フィールド タイプとして使用するメッセージ タイプが別の .proto ファイルですでに定義されている場合はどうなるでしょうか。

他の .proto ファイルの定義を使用するには、ファイルをインポートします。別の .proto の定義をインポートするには、ファイルの先頭に import ステートメントを追加します。

import "myproject/other_protos.proto";

デフォルトでは、直接インポートされた .proto ファイルからの定義のみを使用できます。 ただし、場合によっては、.proto ファイルを新しい場所に移動する必要があります。.proto ファイルを直接移動して、すべてのコールサイトを一度に変更するのではなく、プレースホルダ .proto ファイルを以前の場所に配置することで、すべてのインポートを import public 概念を使用して新しい場所に転送できます。

注: 公開インポート機能は Java では使用できません。

import public 依存関係は、import public ステートメントを含む proto をインポートするすべてのコードで推移的に使用できます。例:

// new.proto
// All definitions are moved here
// old.proto
// This is the proto that all clients are importing.
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto

プロトコル コンパイラは、-I / --proto_path フラグを使用して、プロトコル コンパイラのコマンドラインで指定されたディレクトリ内のインポートされたファイルを検索します。フラグが指定されていない場合、コンパイラが呼び出されたディレクトリを検索します。一般に、--proto_path フラグをプロジェクトのルートに設定し、すべてのインポートで完全修飾名を使用する必要があります。

proto2 メッセージ タイプの使用

proto2 メッセージ タイプをインポートして proto3 メッセージで使用できます。その逆も可能です。ただし、proto2 列挙型を proto3 構文で直接使用することはできません(インポートした proto2 メッセージでそれを使用しても問題ありません)。

ネストされた型

他のメッセージ タイプ内でメッセージ タイプを定義して使用できます。次の例では、Result メッセージが SearchResponse メッセージ内に定義されています。

message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}

このメッセージ タイプを親メッセージ タイプの外部で再利用する場合は、_Parent_._Type_ とします。

message SomeOtherMessage {
  SearchResponse.Result result = 1;
}

メッセージは、次のように必要に応じてネストできます。

message Outer {                  // Level 0
  message MiddleAA {  // Level 1
    message Inner {   // Level 2
      int64 ival = 1;
      bool  booly = 2;
    }
  }
  message MiddleBB {  // Level 1
    message Inner {   // Level 2
      int32 ival = 1;
      bool  booly = 2;
    }
  }
}

メッセージ タイプの更新

既存のメッセージ タイプですべてのニーズを満たせない場合や(メッセージ形式に追加フィールドがある場合でも)は、古い形式で作成したコードを使用することも可能ですのでご安心ください。既存のコードを壊さずに、メッセージ タイプを簡単に更新できます。次のルールを覚えておいてください。

  • 既存のフィールドのフィールド番号は変更しないでください。
  • 新しいフィールドを追加する場合、「古い」メッセージ形式を使用してコードでシリアル化されたメッセージは、新しく生成されたコードによって引き続き解析できます。新しいコードが古いコードによって生成されたメッセージと適切にやり取りできるように、これらの要素のデフォルト値に注意してください。同様に、新しいコードによって作成されたメッセージは、古いコードで解析できます。古いバイナリは、解析時に新しいフィールドを無視するだけです。詳細については、不明なフィールドをご覧ください。
  • 更新後のメッセージ タイプでフィールド番号が再度使用されない限り、フィールドを削除できます。今後フィールド名 .proto を誤って再利用できないように、接頭辞「OBSOLETE_」を追加するか、フィールド番号を予約することをおすすめします。
  • int32uint32int64uint64bool はすべて互換性があります。つまり、上位互換性や下位互換性を損なうことなく、これらのタイプのいずれかからフィールドを変更できます。対応する型に当てはまらないワイヤーから数値が解析される場合、その型を C++ でその型にキャストした場合と同じ効果が得られます(たとえば、64 ビットの数値が int32 として読み取られた場合、32 ビットに切り捨てられます)。
  • sint32sint64 は互いに互換性がありますが、他の整数型と互換性はありません
  • stringbytes は、バイトが有効な UTF-8 である限り、互換性があります。
  • 埋め込みメッセージには、バイト化されているメッセージ バージョンがバイトに含まれている場合に bytes との互換性が確保されます。
  • fixed32sfixed32 と互換性があり、fixed64sfixed64 と互換性があります。
  • stringbytes、メッセージ フィールドの場合、単一フィールドは repeated フィールドと互換性があります。繰り返しフィールドの入力がシリアル化されたデータを入力として使用する場合、プリミティブ型フィールドの場合はクライアントが最後の入力値を取得し、メッセージ型フィールドの場合はすべての入力要素をマージします。通常、ブール値や列挙型などの数値型には安全ではありません。数値型の繰り返しフィールドはパック形式でシリアル化できます。パックされた形式は 1 つのフィールドが想定されていても正しく解析されません。
  • enum は、ワイヤ形式の点で int32uint32int64uint64 と互換性があります(一致しない場合、値は切り捨てられます)。ただし、メッセージがシリアル化解除された場合、クライアント コードでの扱いが異なる可能性があることに注意してください。たとえば、認識されない proto3 enum 型はメッセージに保持されますが、シリアル化解除されたメッセージの表現方法は言語によって異なります。int フィールドの値は常に保持されます。
  • 単一の optional フィールドまたは拡張機能を新しい oneof のメンバーに変更することはバイナリと互換性がありますが、一部の言語(特に Go)では、生成されたコードの API が互換性のない方法で変更されます。このため、AIP-180 に記載されているとおり、Google は公開 API でそのような変更を行いません。ソースの互換性に関する注意点と同様に、複数のフィールドを新しい oneof に移動することは、一度に複数のコードを設定しないことが確実であれば、安全です。既存の oneof にフィールドを移動するのは安全ではありません。同様に、単一のフィールド oneofoptional フィールドまたは拡張機能に変更しても安全です。

不明な項目

不明なフィールドは、整形式のプロトコル バッファでシリアル化されたデータであり、パーサーが認識しないフィールドを表します。たとえば、新しいバイナリによって送信されたデータを新しいフィールドで古いバイナリで解析する場合、新しいフィールドは古いバイナリの不明なフィールドになります。

もともと、proto3 メッセージは解析中に常に不明なフィールドを破棄していましたが、バージョン 3.5 では、proto2 の動作に合わせて未知のフィールドの保持を再度導入しました。バージョン 3.5 以降では、不明なフィールドは解析中に保持され、シリアル化された出力に含まれます。

すべて

Any メッセージ タイプを使用すると、.proto 定義がなくても、メッセージを埋め込みタイプとして使用できます。Any には、任意のシリアル化されたメッセージが bytes として含まれるほか、URL がグローバルに一意の識別子となり、そのメッセージの型に解決される URL が含まれます。Any タイプを使用するには、google/protobuf/any.protoインポートする必要があります。

import "google/protobuf/any.proto";

message ErrorStatus {
  string message = 1;
  repeated google.protobuf.Any details = 2;
}

特定のメッセージ タイプのデフォルトのタイプ URL は type.googleapis.com/_packagename_._messagename_ です。

さまざまな言語の実装で、ランタイム ライブラリ ヘルパーがタイプセーフな方法で Any 値をパックしたりアンパックしたりできるようになります。たとえば、Java では Any 型に特別な pack() アクセサと unpack() アクセサがあり、C++ では PackFrom() メソッドと UnpackTo() メソッドがあります。

// Storing an arbitrary message type in Any.
NetworkErrorDetails details = ...;
ErrorStatus status;
status.add_details()->PackFrom(details);

// Reading an arbitrary message from Any.
ErrorStatus status = ...;
for (const google::protobuf::Any& detail : status.details()) {
  if (detail.Is<NetworkErrorDetails>()) {
    NetworkErrorDetails network_error;
    detail.UnpackTo(&network_error);
    ... processing network_error ...
  }
}

Any 型を操作するランタイム ライブラリは現在開発中です

proto2 の構文に慣れ親しんでいる場合は、Any で、任意の proto3 メッセージを保持できます。proto2 メッセージは拡張を許可できます。

オネオ

メッセージに多数のフィールドがあり、最大 1 つのフィールドが同時に設定される場合は、oneone 機能を使用して、この動作を強制してメモリを節約できます。

1 つのフィールドは、1 つの共有メモリ内のすべてのフィールドを除いて通常のフィールドに似ていますが、同時に設定できるフィールドは 1 つだけです。いずれかのメンバーを設定すると、他のすべてのメンバーは自動的に消去されます。選択した言語に応じて、特別な case() メソッドまたは WhichOneof() メソッドを使用して、いずれかの値が設定されます(存在する場合)。

なお、複数の値を設定した場合、proto の順序によって決定された最後の値によって前の値がすべて上書きされます

Oneof の使用

.proto で oneof を定義するには、oneof キーワードに続いて oneof 名(この場合は test_oneof)を使用します。

message SampleMessage {
  oneof test_oneof {
    string name = 4;
    SubMessage sub_message = 9;
  }
}

次に、oneof 定義に oneof フィールドを追加します。map フィールドと repeated フィールドを除き、任意のタイプのフィールドを追加できます。

生成されたコードでは、ゲッターとセッターが通常のフィールドと同じフィールドを持ちます。また、いずれかに設定されている値(存在する場合)を確認するための特別なメソッドも取得します。選択した言語用の oneof API の詳細については、関連する API リファレンスをご覧ください。

Oneof 機能

  • oneof フィールドを設定すると、oneof の他のすべてのメンバーが自動的に消去されます。複数のフィールドを設定しても、最後に設定したフィールドにのみ値が格納されます。

    SampleMessage message;
    message.set_name("name");
    CHECK_EQ(message.name(), "");
    // Calling mutable_sub_message() will clear the name field and will set
    // sub_message to a new instance of SubMessage with none of its fields set
    message.mutable_sub_message();
    CHECK(message.name().empty());
    
  • パーサーがケーブルの同じメンバーを複数検出した場合、最後に確認されたメンバーのみが解析済みメッセージで使用されます。

  • いずれか 1 つを repeated にすることはできません。

  • リフレクション API はいずれかのフィールドで機能します。

  • oneof フィールドをデフォルト値に設定する(int32 oneof フィールドを 0 に設定するなど)と、その oneof フィールドの「ケース」が設定され、値がネットワーク上でシリアル化されます。

  • C++ を使用している場合は、コードでメモリ クラッシュが発生していないことを確認してください。次のサンプルコードは、set_name() メソッドを呼び出して sub_message がすでに削除されているため、クラッシュします。

    SampleMessage message;
    SubMessage* sub_message = message.mutable_sub_message();
    message.set_name("name");      // Will delete sub_message
    sub_message->set_...            // Crashes here
    
  • C++ でも、ofof を持つ 2 つのメッセージに Swap() を指定すると、各メッセージはどちらか一方のケースになります。次の例では、msg1 には sub_message が含まれ、msg2 には name があります。

    SampleMessage msg1;
    msg1.set_name("name");
    SampleMessage msg2;
    msg2.mutable_sub_message();
    msg1.swap(&msg2);
    CHECK(msg1.has_sub_message());
    CHECK_EQ(msg2.name(), "");
    

下位互換性の問題

フィールドを追加または削除する場合は、注意が必要です。oneof の値の確認で None または NOT_SET が返される場合は、oneof が設定されていないか、oneof の別のバージョンのフィールドに設定されている可能性があります。ワイヤ上の未知のフィールドがそのフィールドのものかどうかを特定する方法がないため、違いを見分ける方法はありません。

タグの再利用に関する問題

  • いずれか 1 つにフィールドを移動する: メッセージのシリアル化および解析後に、一部の情報が失われることがあります(一部のフィールドは消去されます)。ただし、1 つのフィールドを新しいフィールドに安全に移動できます。また、1 つしか設定されていないことがわかっている場合は、複数のフィールドを移動できます。詳細については、メッセージ タイプを更新するをご覧ください。
  • oneof フィールドを削除して追加: メッセージのシリアル化と解析が行われた後に、現在設定されている oneof フィールドが削除される場合があります。
  • 分割または結合: 通常のフィールドを移動する場合と同様の問題があります。

マップ

データ定義の一部として関連付けマップを作成する場合は、プロトコル バッファに便利なショートカット構文が用意されています。

map<key_type, value_type> map_field = N;

ここで、key_type は任意の整数型または文字列型(つまり、浮動小数点型と bytes を除くスカラー型)にすることができます。列挙型は有効な key_type ではないことに注意してください。value_type には、別のマップを除くすべてのタイプを指定できます。

たとえば、各 Project メッセージが文字列キーに関連付けられているプロジェクトのマップを作成する場合は、次のように定義します。

map<string, Project> projects = 3;

  • マップ フィールドは repeated にできません。
  • マップ値の順序とマップ イテレーションは未定義であるため、マップ アイテムが特定の順序にあるという前提にはなりません。
  • .proto のテキスト形式を生成する場合、マップはキーで並べ替えられます。数値キーは数値で並べ替えられます。
  • ケーブルから解析するときやマージするときに、マップキーが重複している場合は最後に使用されたキーが使用されます。テキスト形式からマップを解析するときに、キーが重複すると解析が失敗することがあります。
  • キーは指定しているものの、マップ フィールドに値を指定していない場合、フィールドがシリアル化されたときの動作は言語に依存します。C++、Java、Kotlin、Python では型のデフォルト値がシリアル化されますが、他の言語ではシリアル化されません。

生成されたマップ API は現在、proto3 でサポートされているすべての言語で利用できます。選択した言語の Map API について詳しくは、関連する API リファレンスをご覧ください。

下位互換性

マップ構文は接続例と同等であるため、マップをサポートしないプロトコル バッファ実装はデータを処理できます。

message MapFieldEntry {
  key_type key = 1;
  value_type value = 2;
}

repeated MapFieldEntry map_field = N;

地図をサポートするプロトコル バッファの実装では、上記の定義で受け入れられるデータを生成して受け入れる必要があります。

パッケージ

オプションの package 指定子を .proto ファイルに追加して、プロトコル メッセージ タイプ間の名前の競合を防ぐことができます。

package foo.bar;
message Open { ... }

その後、メッセージ型のフィールドを定義するときにパッケージ指定子を使用できます。

message Foo {
  ...
  foo.bar.Open open = 1;
  ...
}

パッケージ指定子が生成されるコードに与える影響は、選択した言語によって異なります。

  • C++ では、生成されたクラスは C++ 名前空間にラップされます。たとえば、Open は名前空間 foo::bar にあります。
  • JavaKotlin では、.proto ファイルで option java_package を明示的に指定しない限り、パッケージが Java パッケージとして使用されます。
  • Python では、Python モジュールはファイル システム内の位置に従って編成されるため、パッケージ ディレクティブは無視されます。
  • Go では、.proto ファイルに option go_package を明示的に指定しない限り、パッケージが Go パッケージ名として使用されます。
  • Ruby では、生成されたクラスがネストされた Ruby 名前空間にラップされ、必要な Ruby 大文字表記(最初の文字が大文字になる)に変換されます。最初の文字が文字でない場合は、PB_ が追加されます。たとえば、Open は名前空間 Foo::Bar にあります。
  • C# では、.proto ファイルで option csharp_namespace を明示的に指定しない限り、PascalCase に変換された後、パッケージが名前空間として使用されます。たとえば、Open は名前空間 Foo.Bar にあります。

パッケージと名前解決

プロトコル バッファ言語の型の名前解決は C++ のような働きをします。まず最も内側のスコープが検索され、次に次に最も内側のスコープが検索され、以降、同じように親パッケージの「内部」とみなされます。先頭の「.」(例: .foo.bar.Baz)は、代わりに最も外側のスコープから開始することを意味します。

プロトコル バッファ コンパイラは、インポートされた .proto ファイルを解析することですべての型名を解決します。言語ごとのジェネレータは、スコープルールが異なる場合でも、その言語で各タイプを参照する方法を認識します。

サービスの定義

RPC(Remote Procedure Call)システムでメッセージ タイプを使用する場合は、.proto ファイルで RPC サービス インターフェースを定義すると、プロトコル バッファ コンパイラが選択した言語でサービス インターフェース コードとスタブを生成します。たとえば、SearchRequest を受け取って SearchResponse を返すメソッドで RPC サービスを定義する場合は、.proto ファイルで次のように定義できます。

service SearchService {
  rpc Search(SearchRequest) returns (SearchResponse);
}

プロトコル バッファとともに使用する最も単純な RPC システムは gRPC です。gRPC は、Google で開発された、言語とプラットフォームに中立なオープンソース RPC システムです。gRPC はプロトコル バッファで特に適切に動作し、特別なプロトコル バッファ コンパイラ プラグインを使用して .proto ファイルから直接、関連する RPC コードを生成できます。

gRPC を使用しない場合は、独自の RPC 実装でプロトコル バッファを使用することもできます。詳細については、Proto2 言語ガイドをご覧ください。

また、プロトコル バッファ用の RPC 実装を開発するためのサードパーティ プロジェクトも多数あります。既知のプロジェクトへのリンクについては、サードパーティのアドオン Wiki ページをご覧ください。

JSON マッピング

Proto3 では、JSON の正規エンコードがサポートされています。これにより、システム間でのデータ共有が簡単になります。エンコードについては、以下の表をご覧ください。

JSON でエンコードされたデータをプロトコル バッファに解析するときに、値が欠落している場合、または値が null の場合は、対応するデフォルト値として解釈されます。

プロトコル バッファから JSON エンコードの出力を生成する際、protobuf フィールドの値が {0/} で、フィールド フィールドがサポートされていない場合、デフォルトでは出力から省略されます。実装では、デフォルト値を持つフィールドを出力に含めることができます。

optional キーワードで定義された proto3 フィールドは、フィールドの有無をサポートしています。値が設定され、フィールドの存在をサポートするフィールドでは、デフォルト値であっても、常に JSON エンコード出力にフィールド値が含まれます。

proto3 JSON JSON の例 備考
メッセージ オブジェクト {"fooBar": v, "g": null, …} JSON オブジェクトを生成します。メッセージ フィールド名が LowCamelCase にマッピングされ、JSON オブジェクト キーになります。json_name フィールド オプションを指定すると、指定した値がキーとして使用されます。パーサーは、lowerCamelCase の名前(または json_name オプションで指定された名前)と元の proto フィールド名の両方を受け入れます。null はすべてのフィールド タイプの値として受け入れられ、対応するフィールド タイプのデフォルト値として扱われます。
enum 文字列 "FOO_BAR" proto で指定された列挙型の名前が使用されます。パーサーは列挙型名と整数値の両方を受け入れます。
map<K,V> オブジェクト {"k": v, …} すべてのキーは文字列に変換されます。
繰り返し V 配列 [v, …] null は空のリスト [] として受け入れられます。
ブール値 true、false true, false
文字列 文字列 "Hello World!"
バイト base64 文字列 "YWJjMTIzIT8kKiYoKSctPUB+" JSON 値は、標準の Base64 エンコードとパディングを使用して、文字列としてエンコードされたデータになります。パディングあり/なしの、標準または URL セーフの Base64 エンコードを使用できます。
int32、fixed32、uint32 数値 1, -10, 0 JSON 値は 10 進数になります。数字または文字列を使用できます。
int64、fixed64、uint64 文字列 "1", "-10" JSON 値は 10 進数の文字列です。数字または文字列を使用できます。
浮動小数点数、倍精度 数値 1.1, -10.0, 0, "NaN", "Infinity" JSON 値は数値、あるいは特殊な文字列値「NaN」、「Infinity」、「-Infinity」のいずれかになります。数字または文字列を使用できます。指数表記も使用できます。-0 は 0 と同等とみなされます。
すべて object {"@type": "url", "f": v, … } Any に特別な JSON マッピングを持つ値が含まれている場合、{"@type": xxx, "value": yyy} に変換されます。それ以外の場合、値は JSON オブジェクトに変換され、"@type" フィールドが挿入されて実際のデータ型が示されます。
タイムスタンプ 文字列 "1972-01-01T10:00:20.021Z" RFC 3339 を使用します。ここで生成される出力は常に Z 正規化され、0、3、6、9 小数点が使用されます。「Z」以外のオフセットも使用できます。
所要時間 文字列 "1.000340012s", "1s" 生成される出力には、必要な精度に応じて小数点以下が 0 桁、3 桁、6 桁、または 9 桁が含まれ、その後に接尾辞「s」が続きます。小数点以下も使用可能で、接尾辞「s」が含まれている限り、小数点以下何桁も使用可能です。
構造体 object { … } 任意の JSON オブジェクト。struct.protoをご確認ください。
ラッパータイプ さまざまな種類 2, "2", "foo", true, "true", null, 0, … ラッパーは、ラップされたプリミティブ型と同じ JSON 表現を使用しますが、データ変換と転送中に null が許可され、保持されます。
FieldMask 文字列 "f.fooBar,h" field_mask.protoをご確認ください。
ListValue 配列 [foo, bar, …]
任意の JSON 値。詳しくは、google.protobuf.Value をご覧ください。
NullValue null JSON の null
空白 オブジェクト {} 空の JSON オブジェクト

JSON のオプション

proto3 JSON 実装には、次のオプションが用意されています。

  • デフォルト値を持つフィールドの出力: proto3 JSON 出力では、デフォルト値を持つフィールドはデフォルトで省略されます。実装では、この動作をオーバーライドし、フィールドをデフォルト値で出力するオプションを提供できます。
  • 不明なフィールドを無視する: Proto3 JSON パーサーはデフォルトで不明なフィールドを拒否しますが、解析時に不明なフィールドを無視するオプションを提供することもできます。
  • lowerCamelCase 名の代わりに proto フィールド名を使用する: デフォルトでは、proto3 JSON プリンタは、フィールド名を lowerCamelCase に変換し、それを JSON 名として使用する必要があります。実装では、JSON 名として proto フィールド名を使用するオプションを提供できます。Proto3 JSON パーサーは、変換された lowerCamelCase 名と proto フィールド名の両方を受け入れる必要があります。
  • 列挙型の値を文字列ではなく整数として出力する: 列挙値の名前は、JSON 出力内でデフォルトとして使用されます。代わりに、列挙値の数値を使用するオプションを指定することもできます。

オプション

.proto ファイル内の個々の宣言には、複数のオプションをアノテーションできます。オプションにより宣言の全体的な意味は変わりませんが、特定のコンテキストでの処理方法に影響する可能性があります。使用可能なオプションの一覧については、/google/protobuf/descriptor.proto をご覧ください。

一部のオプションはファイルレベルのオプションです。つまり、メッセージ、列挙型、サービス定義の内部ではなく、最上位のスコープで記述する必要があります。一部のオプションはメッセージ レベルのオプションであり、メッセージ定義内に記述する必要があります。一部のオプションはフィールド レベルのオプションであり、フィールド定義内に記述する必要があります。オプションは、列挙型、列挙値、フィールドの 1 つ、サービスタイプ、サービス メソッドで記述することもできますが、いずれのオプションも現在のところ使用できません。

よく使用されるオプションをいくつか紹介します。

  • java_package(ファイル オプション): 生成された Java/Kotlin クラスに使用するパッケージ。.proto ファイルに明示的な java_package オプションが指定されていない場合、デフォルトで proto パッケージ(.proto ファイルの「package」キーワードを使用して指定)が使用されます。ただし、proto パッケージはリバース ドメイン名から始める必要がないため、一般的には proto パッケージは優れた Java パッケージとは言えません。Java コードも Kotlin コードも生成しない場合、このオプションは無視されます。

    option java_package = "com.example.foo";
    
  • java_outer_classname(ファイル オプション): 生成するラッパー Java クラスのクラス名(したがってファイル名)。.proto ファイルで明示的な java_outer_classname が指定されていない場合、.proto ファイル名をキャメルケースに変換することでクラス名が作成されます(foo_bar.protoFooBar.java になります)。java_multiple_files オプションが無効になっている場合、.proto ファイルに対して生成された他のクラス / 列挙型などはすべて、この外部ラッパー Java クラスをネストされたクラス / 列挙型などとして生成します。Java コードを生成しない場合、この効果はありません。

    option java_outer_classname = "Ponycopter";
    
  • java_multiple_files(ファイル オプション): false の場合、この .proto ファイルに対して生成される .java ファイルは 1 つだけであり、トップレベル メッセージ、サービス、列挙型について生成されるすべての Java クラス / 列挙型などは、外部クラス内にネストされます(java_outer_classname を参照)。true の場合、Java クラス / 列挙型などでグループ化された最大クラス / 列挙型のメッセージがJava コードを生成しない場合、このオプションは無効になります。

    option java_multiple_files = true;
    
  • optimize_for(ファイル オプション): SPEEDCODE_SIZELITE_RUNTIME のいずれかに設定できます。C++ と Java のコード生成ツール(場合によってはサードパーティの生成ツール)には、次の影響があります。

    • SPEED(デフォルト): プロトコル バッファ コンパイラは、メッセージ タイプに対するシリアル化、一般的な解析、その他の一般的なオペレーションのためのコードを生成します。このコードは高度に最適化されています。
    • CODE_SIZE: プロトコル バッファ コンパイラは最小限のクラスを生成し、リフレクション ベースの共有コードに依存してシリアライゼーション、解析、その他のさまざまなオペレーションを実装します。したがって、生成されるコードは SPEED よりもはるかに少なくなりますが、オペレーションは遅くなります。クラスは、SPEED モードとまったく同じ公開 API を実装します。このモードは、非常に多くの .proto ファイルが含まれ、それらすべてを高速に処理できるわけではないアプリで非常に便利です。
    • LITE_RUNTIME: プロトコル バッファ コンパイラは、「lite」ランタイム ライブラリのみに依存するクラスを生成します(libprotobuf ではなく libprotobuf-lite)。Lite ランタイムはライブラリ全体よりもはるかに小さくなります(約 1 桁小さくなります)。ただし、記述子やリフレクションなどの特定の機能は省略されます。これは、スマートフォンなど、制約のあるプラットフォームで実行されるアプリに対して特に便利です。コンパイラは、SPEED モードの場合と同様に、すべてのメソッドの高速実装を生成します。 生成されるクラスは各言語の MessageLite インターフェースのみを実装します。これにより、完全な Message インターフェースのメソッドのサブセットのみが提供されます。
    option optimize_for = CODE_SIZE;
    
  • cc_enable_arenas(ファイル オプション): C++ で生成されたコードのアリーナ割り当てを有効にします。

  • objc_class_prefix(ファイル オプション): この .proto から作成されたすべての Objective-C クラスと列挙型の前に Objective-C クラスの接頭辞を設定します。デフォルト値はありません。Apple が推奨する 3 ~ 5 文字の大文字を使用する必要があります。2 文字のプレフィックスはすべて Apple によって予約されています。

  • deprecated(フィールド オプション): true に設定すると、このフィールドは非推奨になり、新しいコードで使用されなくなります。ほとんどの言語では、実際に影響はありません。Java では、これは @Deprecated アノテーションになります。C++ では、clang-tidy を使用すると、サポートが終了したフィールドが使用されるたびに警告が生成されます。将来的には、他の言語固有のコード生成ツールがフィールドのアクセサで非推奨アノテーションを生成する可能性があります。これにより、フィールドを使用するコードをコンパイルする際に警告が発せられます。このフィールドが誰も使用しておらず、新しいユーザーがフィールドを使用できないようにする場合は、フィールド宣言を予約済みステートメントに置き換えることを検討してください。

    int32 old_field = 6 [deprecated = true];
    

カスタマイズオプション

プロトコル バッファを使用すると、独自のオプションを定義して使用することもできます。これは、ほとんどのユーザーにとって不要な高度な機能です。独自のオプションを作成する必要があると思われる場合は、Proto2 言語ガイドをご覧ください。カスタム オプションを作成する際は、拡張機能を使用します。これは、proto3 のカスタム オプションでのみ許可されています。

クラスの生成

.proto ファイルで定義されたメッセージ型を操作する必要がある Java、Kotlin、Python、C++、Go、Ruby、Objective-C、C# コードを生成するには、.proto でプロトコル バッファ コンパイラ protoc を実行する必要があります。コンパイラをインストールしていない場合は、パッケージをダウンロードし、README の指示に従ってください。Go の場合、コンパイラ用の特別なコード生成プラグインもインストールする必要があります。このプラグインとインストール手順は、GitHub の golang/protobuf リポジトリで確認できます。

プロトコル コンパイラは次のように呼び出されます。

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto
  • IMPORT_PATH は、import ディレクティブを解決する際に .proto ファイルを探すディレクトリを指定します。省略した場合、現在のディレクトリが使用されます。--proto_path オプションを複数回渡すことで、複数のインポート ディレクトリを指定できます。これらのディレクトリは順番に検索されます。-I=_IMPORT_PATH_ は、--proto_path の短縮形として使用できます。
  • 以下の 1 つ以上の出力ディレクティブを指定できます。

    追加の便宜上、DST_DIR.zip または .jar で終わる場合、コンパイラは出力を、指定された名前の単一の ZIP 形式のアーカイブ ファイルに書き込みます。また、.jar 出力には、Java JAR 仕様で必要とされるマニフェスト ファイルも提供されます。出力アーカイブがすでに存在する場合、そのアーカイブは上書きされます。コンパイラは、既存のアーカイブにファイルを追加できないほどスマートです。

  • 1 つ以上の .proto ファイルを入力として提供する必要があります。一度に複数の .proto ファイルを指定できます。ファイル名は現在のディレクトリからの相対名ですが、コンパイラが正規名を識別できるように、各ファイルは IMPORT_PATH のいずれかに存在している必要があります。

ファイルの場所

他の言語のソースと同じディレクトリに .proto ファイルを配置しないようにします。プロジェクトのルート パッケージの下に、.proto ファイル用のサブパッケージ proto を作成することを検討してください。

地域は言語に依存しない必要がある

Java コードを操作するときに、関連する .proto ファイルを Java ソースと同じディレクトリに置くと便利です。ただし、Java 以外のコードで同じ proto を使用する場合は、パスのプレフィックスは意味をなさなくなります。一般に、proto は、//myteam/mypackage など、言語に依存しない関連するディレクトリに配置します。

このルールの例外は、テストに Java コンテキストでのみ proto が使用されることが明らかである場合です。

サポートされているプラットフォーム

詳細情報: