
概略
.proto ファイルを作成し、protoc というコンパイラで各言語向けのスタブファイルを作成します。
helloworld.proto の例は以下のようなものです。
syntax = "proto3"; package helloworld; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
service ブロックに rpc キーワードでRPC(Remote Procedure Call) のインターフェースを定義します。
message キーワードで、やり取りするメッセージフォーマットを定義します。
ファイルの先頭には syntax キーワードで構文を指定します。
指定しなかった場合には proto2 が指定されたものとして扱われます。
package キーワードでパッケージを定義します。
コメントは C形式のコメントが利用できます。
行コメントには //、ブロックコメントには /* */ を使います。
メッセージタイプ
メッセージにはフィールドを定義できます。
message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; }
フィールドにはフィールド番号を割り当てます。
この番号はメッセージバイナリ形式での識別子として利用されます。
フィールドを削除した場合などで、削除済みのフィールドを再利用されないようにするには reserved キーワードを使うことでフィールド番号やフィールド名を予約することができます。
message Foo { reserved 2, 15, 9 to 11; reserved "foo", "bar"; }
proto2 では required や optional が使えたが proto3 で廃止されました。
スカラー値型
スカラー値型は以下のようなものが利用でき、コンパイル後の各言語との対応は以下のようになります。
| .proto Type | Java Type | Python Type | Go Type | C# Type |
|---|---|---|---|---|
| double | double | float | float64 | double |
| float | float | float | float32 | float |
| int32 | int | int | int32 | int |
| int64 | long | int/long | int64 | long |
| uint32 | int | int/long | uint32 | uint |
| uint64 | long | int/long | uint64 | ulong |
| sint32 | int | int | int32 | int |
| sint64 | long | int/long | int64 | long |
| fixed32 | int | int/long | uint32 | uint |
| fixed64 | long | int/long | uint64 | ulong |
| sfixed32 | int | int | int32 | int |
| sfixed64 | long | int/long | int64 | long |
| bool | boolean | bool | bool | bool |
| string | String | str/unicode | string | string |
| bytes | ByteString | str | []byte | ByteString |
サービス
RPC サービスのインターフェイスを以下のように定義できます。
service SearchService { rpc Search (SearchRequest) returns (SearchResponse); }
SearchRequest を引数に取り SearchResponse を返す RPC サービスを定義しています。
コンパイラは選択した言語でサービスのインターフェイスとスタブを生成します。
入れ子のメッセージ
メッセージは他のメッセージを使い構造化することができます。
message SearchResponse { Result results = 1; } message Result { string url = 1; string title = 2; }
メッセージ内に直接定義することもできます。
message SearchResponse { message Result { string url = 1; string title = 2; repeated string snippets = 3; } repeated Result results = 1; }
内部のメッセージは以下のようにピリオドでつないで使うことができます。
message SomeOtherMessage { SearchResponse.Result result = 1; }
enum
enum キーワードで enum を定義できます。
enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; }
フィールド番号として、必ず最初に 0 を指定する必要があります。
フィールド番号 0 のものがデフォルト値として扱われます。
メッセージの中に入れ子で定義することもできます。
message SearchRequest { string query = 1; int32 page_number = 2; enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; } Corpus corpus = 4; }
なお、reserved キーワードも前述の通りに使うことができます。
repeated
repeated でリストを定義できます。
message SearchResponse { repeated Result results = 1; } message Result { string url = 1; string title = 2; repeated string snippets = 3; }
map
map でマップを定義できます。
map<string, Project> projects = 3;
map は repeated で修飾することはできません。
oneof
たくさんのフィールドがあり、その内の 1 つだけに値が設定される場合は oneof を使うことでメモリを節約することができます。
message SampleMessage { oneof test_oneof { string name = 4; SubMessage sub_message = 9; } }
proto3 で Optional を表す代替として oneof を使うことができます。
以下のようにフィールドが 1 つの oneof を定義します。
message Request { oneof option { int64 option_value = 1; } }
以下のように Optional を模倣することができます。
Request.OptionCase optionCase = request.getOptionCase(); OptionCase optionNotSet = OPTION_NOT_SET; if (optionNotSet.equals(optionCase)){ // value not set } else { // value set }
デフォルト値
やり取りするメッセージに特定の要素が含まれていない場合、そのフィールドのデフォルト値が適用されます。
- 文字列:空の文字列
- バイト:空のバイトです。
- bool:false
- 数値型:ゼロ
- enums:最初に定義されたフィールド番号 0 の enum値
- メッセージフィールド:設定されない(言語に依存)
- 繰り返しフィールド:空
パッケージ
名前のコンフリクトを防ぐために package キーワードでパッケージを定義することができます。
package foo.bar; message Open { ... }
以下のようにドットで連結して参照することができます。
foo.bar.Open open = 1;
パッケージは、生成されたコードの言語で扱いがことなります。
Go や Java はそのままパッケージとして扱われます(option go_package や option java_package でソース上のパッケージを上書することができます)。
インポート
他の .proto の定義をインポートするには するには、import キーワードで他の .proto ファイルを指定します。
import "myproject/other_protos.proto";
インポートすると、この .proto 内でのみ インポートした定義を利用することができます。
さらに外側の .proto までインポートした定義を公開するには以下のように public キーワードを指定します。
import public "myproject/other_protos.proto";
これによりインポートした定義を他の .proto に推移させることができます。
オプション
.proto ファイルには option キーワードで注釈をつけることができます。
生成されたJavaクラスに使用するパッケージを指定するには以下のようにします。
option java_package = "com.example.foo";
デフォルトではパッケージのクラスの内部クラスとしてメッセージやサービスが定義されますが、以下の指定をすることで、内部クラスではなく通常のクラスとしてコード生成されます。
option java_multiple_files = true;
生成する外部クラス名を指定するには以下のオプションを設定します。
option java_outer_classname = "Ponycopter";
コンパイラオプション
protoc コンパイラにて .proto ファイルからコード生成を行います。
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 には .proto を探すディレクトリを指定します(省略すると現在のディレクトリ)。
--proto_path は -I のように省略形を指定することもできます。
DST_DIR は出力先のディレクトリを指定します。それぞれ出力先の言語を並べて指定することができます。
--cpp_outで C++ コードを生成--java_outで Java コードを生成--python_outで Python コードを生成--go_outで Go コードを生成--ruby_outで Ruby コードを生成--objc_outで Objective-C コードを生成--csharp_outで C#コードを生成--php_outで PHP コードを生成