このガイドでは、protocol buffers データを構造化するのに使う、Protocol Buffers 言語の使い方について述べます。 その中には、.protoファイルの構文と、どうやって .protoファイルからデータアクセスクラスを生成するのかについての説明も含みます。
この文書はリファレンスガイドです。 この文書で述べられる機能の多くを利用する段階的なサンプルは、あなたの言語向けの チュートリアルを参照してください。
まず最初に、とても単純なサンプルを見ましょう。 検索リクエストのメッセージ形式を定義したいとします。 検索リクエストには、検索文字列、取得したい結果のページ数、一ページに含む結果の数を持たせます。 これが、このメッセージ型を定義した .proto ファイルになります。
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}
SearchRequest メッセージ定義は 3つのフィールド(名前と値のペア)を指定しています。 それぞれのフィールドは、このメッセージ型に含めたいデータの一部分になっています。 フィールドは名前と型を持ちます。
上の例では、全てのフィールドは二つの整数型(page_number, result_per_page)と、文字列型(query)といったスカラー型です。 列挙型や他のメッセージ型といった、複合型を指定することもできます。
見て分かるように、個々のフィールドは **ユニークな数値のタグ**を持っています。 それらのタグは メッセージのバイナリ形式で、フィールドを識別するのに用いられます。 メッセージを使い始めたら、このタグの値は変更すべきではありません。 1から15までの値のタグは、1バイトにエンコードされます。16から 2047までは2バイトになります。 ですので、1から15までのタグは、メッセージで頻出する要素に予約すべきです。 頻出要素には、将来追加できるように少し余裕を持たせるのを忘れないでください。
割り当てられるタグの最小値は 1 で、最大値は 2^29-1(もしくは 536,870,911)です。 19000から19999まで(FieldDescriptor::kFirstReservedNumer から FieldDescriptor::kLastReservedNumber)の値は使えません。 これらの値は、Protocol Buffers の実装用に予約されています。 これらの値を .proto ファイルで使おうとすると、protocol buffer コンパイラは警告を発するでしょう。
一つの .proto ファイル中に、複数のメッセージ型を定義することができます。 これは関連するメッセージ群を定義する時に有用です。 たとえば、SearchResponse メッセージ型といった応答メッセージを定義したい場合、それを同一の .proto ファイルに追加することができます。
</p><pre>message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}
message SearchResponse {
...
}
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;// 欲しいページ数
optional int32 result_per_page = 3;// ページ内の結果数
}
メッセージを使うプログラミング言語を選び、.protoファイルに protocol bufferコンパイラをかけると、 コンパイラはその言語用のコードを出力します。 生成コードには、フィールドの値を取得/指定するコード、 メッセージを出力ストリームへ直列化するコード、 入力ストリームからメッセージへ解析するコードが含まれています。
C++ 向けには、.protoファイルに記述されたそれぞれのメッセージ型のクラスがある .h と .cc ファイルを生成します。
Java 向けには、それぞれのメッセージ型のクラスと、クラスをインスタンス化するための特別な Builder クラスがある .java ファイルを生成します。
Python は少し異なっています。Pythonコンパイラは .protoファイルのメッセージ型のそれぞれの static descriptor のあるモジュールを生成します。これは実行時に Pythonのデータアクセスクラスを生成するためにメタクラスと共に用いられます。
これ以上の API 使い方については、それぞれの言語のチュートリアルで得られます。 API のさらなる詳細は、関連する APIリファレンスを参照してください。
単独のメッセージフィールドは以下のいずれかの型になります。 表には、.proto ファイルで指定する型と、自動生成クラスで対応する型を示しています:
| .proto型 | 説明 | C++型 | Java型 |
|---|---|---|---|
| double | double | double | |
| float | float | float | |
| int32 | 可変長エンコーディング。負の数のエンコードは効率が悪い。負の数をよく使うフィールの場合は sint32 を使うこと。 | int64 | long |
| uint32 | 可変長エンコーディング。 | uint32 | int |
| uint64 | 可変長エンコーディング。 | uint64 | long |
| sint32 | 可変長エンコーディング。符号付き整数。int32よりも負の数のエンコード効率がよい。 | int32 | int |
| sint64 | 可変長エンコーディング。符号付き整数。int32よりも負の数のエンコード効率がよい。 | int64 | long |
| fixed32 | 常に 4byte. 値が 2^28 を頻繁に超える場合は uint32 より効率がよい。 | uint32 | int |
| fixed64 | 常に 8byte. 値が 2^56 を頻繁に超える場合は uint64 より効率がよい。 | uint64 | long |
| sfixed32 | 常に 4byte. | int32 | int |
| sfixed64 | 常に 8byte. | int64 | long |
| bool | bool | bool | |
| string | UTF-8 か 7-bitアスキー文字の文字列。 | string | String |
| bytes | 任意長のバイト列 | string | ByteString |
これらの型はメッセージの直列化でどのようにエンコードされるかについてのさらなる情報は Protocol Buffer エンコーディングで見つけることができる。
上で触れたように、メッセージ記述では要素を optional とラベル付けることができる。 整形式のメッセージはオプション要素を含んでもよいし、含まなくてもよい。 メッセージを解析するときに、オプション要素が含まれていなかったら、 解析後のオブジェクトの対応するフィールドは、そのフィールドのデフォルトの値にセットされる。 デフォルト値はメッセージ記述の一部として指定することができる。 例えば、SearchRequest の result_per_page にデフォルト値の 10 を与えよう:
optional int32 result_per_page = 3 [default = 10];
オプション要素にデフォルト値が指定されていなかったら、型に応じたデフォルト値が使われる: 文字列型のデフォルト値は空文字列である。真偽値のデフォルト値は偽である。 数値型のデフォルト値はゼロである。
メッセージを定義するときに、あるフィールドを事前に定義した値リストのいずれかに制限したいかもしれない。 例として、SearchRequest に corpus フィールドを追加する。corpus は UNIVERSAL, WEB, IMAGES, LOCAL, NEWS, PRODUCTS, VIDEO のいずれかの値をとる。 これを行うのは、enum をメッセージ定義に追加することで簡単にできる。 enum 型のフィールドは、その値として指定した定数セットのひとつのみを持てる(異なった値を与えたら、パーザはそれを未知のフィールドのように扱う)。 次の例で、全ての可能な値を持った Corpus という列挙型を追加し、フィールドの型を Corpus にしている:
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3 [default = 10];
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
optional Corpus corpus = 4 [default = UNIVERSAL];
}
列挙型の定数は [0, 2147483637] の範囲内でなければならない。 列挙型は上の例のようにメッセージ定義の中で定義することもできるし、外に書くこともできる。 外に書いた列挙型は .proto ファイル中のどのメッセージでも再利用することができる。 あるメッセージの中で宣言された列挙型も、 *MessageType*. *EnumType* の構文を使えば他のメッセージのフィールドの型に使うことができる。
enum を使った .protoファイルを protocol buffer コンパイラをかけると、Java と C++ の生成コードは対応する enum を持つ。Python では、特別な EnumDescriptor クラスを持ち、これは実行時に生成されるクラス中で象徴的な数値定数のセットを作るのに使われる。 アプリケーションで enum を使うためのさらなる情報については、それぞれの言語の 生成コードガイドを参照すること。
他のメッセージ型をフィールドの型として使うことができる。 例えば、Resultメッセージを SearchResponseメッセージに含めたいとする。 これを行うには、Resultメッセージ型を同じ .proto で定義して、SearchResponse のフィールドの型を Result で指定する:
message SearchResponse {
repeated Result result = 1;
}
message Result {
required string url = 1;
optional string title = 2;
repeated string snippets = 3;
}
上の例では、Resultメッセージ型は SearchResponse と同じファイル中で定義された。 既に他の .proto ファイルで定義されたメッセージ型を使いたい場合はどうすればよいのでしょう?
他の.proto ファイルの定義を *取りこむ* ことで、その定義を使うことができます。 他の .protoファイルの定義を取りこむには、import文をファイルの先頭に追加します:
import "myproject/other_protos.proto";
プロトコルコンパイラは、コマンドラインの -I か --import_path フラグで指定されたディレクトリから、import ファイルを探します。フラグが与えられなかったら、コンパイラの起動されたディレクトリで探します。
以下の例のように、メッセージ型の中でメッセージ型を定義したり使用したりすることができます。 例では、Resultメッセージが SearchResponseメッセージの中で定義されています:
message SearchResponse {
message Result {
required string url = 1;
optional string title = 2;
repeated string snippets = 3;
}
repeated Result result = 1;
}
このメッセージを親メッセージの外で使いたい場合は、 *Parent*. *Type* で参照します。
message SomeOtherMessage {
optional SearchResponse.Result result = 1;
}
message Outer { // Level 0
message MiddleAA { // Level 1
message Inner { // Level 2
required int64 ival = 1;
optional bool booly = 2;
}
}
message MiddleBB = { // Level 1
message Inner = { // Level 2
required int32 ival = 1;
optional bool booly = 2;
}
}
}
この機能は廃止予定なので、新たなメッセージを作る際には使用すべきではありません。代わりにネストされたメッセージ型を使ってください
既存のメッセージ型がもはや要求を満たさなくなった場合、例えばメッセージが余分なフィールドを持たせたい場合、 だけれども古い定義で作られたコードを使い続けたい場合、心配することはありません! 既存のコードをいささかも壊さずにメッセージ型を更新することは、とても簡単です。 次の規則を思い出してください:
後から追加する新たなフィールドは optional か repeated にすべき。 これは、「古い」メッセージ形式を使ったコードで直列化されたメッセージが、required要素を欠かない限り新たに生成されたコードでも解析できることを意味します。新しいコードが古いコードから作られたメッセージと適切に対話できるように、これらの新しいフィールドには意味のある デフォルト値を設定すべきです。同様に、新たなコードから作られたメッセージは古いコードで解析できます: 古いバイナリは、解析時に新たなフィールドをただ単に無視します。しかし、未知のフィールドは捨てられず、メッセージが後で直列化される際には未知のフィールドはそのまま直列化されます。ですので、このメッセージが新しいコードに渡されるとしても、新しいフィールドは依然として利用可能です。現時点では、未知のフィールドの保護は Python では使えません。
タグ番号が更新されたメッセージで再利用されない限り、required でないフィールドは取り除けます(それよりは、.protoファイルの将来の利用者がたまたま番号を再利用してしまわないように、フィールドの名前を OBSOLETE_ プリフィクスを付けるなど変更するのが良いでしょう。)。
型と番号が同じである限り、require でないフィールドは extension へ変換できますし、逆もできます。
int32, uint32, int64, uint64, bool は互換性があります。これらの間での型の変更は、前方互換性も後方互換性を壊しません。バイナリ表現から数値を解析してみて対応する型に合わない場合は、その数値をその型に C++ キャストするのと同じ効果を得ます(例えば、64bit整数を int32として読んだ場合は、32bit に丸めこまれます)。
sint32 と sint64 はお互いに互換性がありますが、他の整数型とは互換性がありません。
bytes が UTF-8 として正しい限りにおいて、string と bytes には互換性があります。
bytes にメッセージのエンコード版が含まれていれば、組み込まれたメッセージは bytes と互換性があります。
fixed32 は sfixed32 と、fixed64 は sfixed64 と互換性があります。
extension を使えば、メッセージ中のあるフィールド番号範囲を第三者の拡張に利用可能と宣言できます。 他の人々は、オリジナルファイルを変更せずに、彼らの .proto ファイル中で、あなたのメッセージ型に拡張用番号のタグを持った新たなフィールドを宣言することができます。 例を見てみましょう:
message Foo {
// ...
extensions 100 to 199;
}
これは Foo の [100, 199] のフィールド番号を拡張用に予約すると言っています。 これで、他のユーザーは自分の .proto ファイルであなたの .proto ファイルを import して、Foo に新たなフィールドを追加することができます。そのタグはあなたの指定した範囲のものです。例示すると:
extend Foo {
optional int32 bar = 126;
}
これは、これからは Foo は bar という optional な int32型のフィールドを持つことを宣言しています。 ユーザーの Foo メッセージをエンコードしたら、バイナリ形式は Foo に新たなフィールドを定義したのと同じになります。 しかし、アプリケーションで拡張フィールドをアクセスする方法は通常のフィールドとは少し異なります。 生成されたデータアクセスコードには、拡張用の特別なアクセサがあります。C++ で値をセットする例を示しましょう:
Foo foo; foo.SetExtension(bar, 15);
同様に、Fooクラスにも HasExtension(), ClearExtension(), GetExtension(), MutableExtension(), AdDExtension() というテンプレートアクセサが定義されます。 これら全ては、通常のフィールド用に生成された対応するアクセサのセマンティクスと一致します。 extension を使うためのさらなる情報は、生成された言語用のリファレンスを参照してください。
extension は、メッセージ型を含め、どんなフィールド型にもなります。
message Baz {
extend Foo {
optional int32 bar = 126;
}
...
}
Foo foo; foo.SetExtension(Baz::bar, 15);
言い換えると、この機能の唯一の効果は、Baz のスコープの中で定義することです。
これは混乱の原因となります: メッセージの中にネストした extend ブロックの宣言は、外側の型と extend 型との間で何の関係も *含みません*。 実際に、上の例は Baz は Foo のどんな種類のサブクラスでも *ありません*。 この例の意味することの全ては、bar が Baz スコープの中で宣言されているということです。これは単なる static メンバです。
message Baz {
extend Foo {
optional Baz foo_ext = 127;
}
...
}
message Baz {
...
}
// これを別のファイルに書くことすら可能です
extend Foo {
optional Baz foo_baz_ext = 127;
}
実際は、混乱を避けるためにこの構文の方が好まれるかもしれません。 上で触れたように、extension に不慣れなユーザーにとって、ネストされた構文はしばしばサブクラスと取り違えられます。
二人のユーザーが、同じメッセージに同じ番号のタグで拡張を追加しないようにすることはとても重要です。 拡張が偶然に間違った型として解釈されると、データの破壊されてしまいます。 このようなことを避けるために、プロジェクトでは拡張番号規約を定義することを考えるでしょう。
番号規約では、拡張はかなり広範囲の番号を割り当てるでしょう。 この場合、max キーワードを用いて、フィールド番号の最大値まで拡張番号の範囲に指定することができます。
message Foo {
extensions 1000 to max;
}
max は 2^29 - 1、もしくは 536,870,911 です。
一般的にタグ番号を選択する際には、番号付け規約は 19000から19999(FieldDescriptor::kFirstReservedNumber から FieldDescriptor::kLastReservedNumber)の範囲の番号を避ける必要があります。 これらは Protocol Buffers 実装用に予約されています。 これらの範囲を含んだ拡張範囲を定義することは可能ですが、これらの数を実際に指定することはプロトコルコンパイラが許可しません。
プロトコルメッセージ型の衝突を避けるために、.proto ファイルにオプションの package 指定を 追加することができます。
package foo.bar;
message Open { ... }
message Foo {
...
required foo.bar.Open open = 1;
...
}
C++ では、生成されたクラスは C++名前空間の中に包まれます。例えば、Open は foo::bar 名前空間の中になります。
Java では、.protoファイルで明示的に java_packageオプションを与えなければ、パッケージには Java の package が使われます。
Python では、Pythonモジュールはファイルシステム中の配置に従って組織化されるので、package指定は無視されます。
メッセージ型を RPC(Remote Procedure Call)システムで使いたい場合は、.proto ファイルに RPCサービスインターフェースを定義できます。 protocol buffer コンパイラは、選択した言語用にサービスインターフェースのコードとスタブを生成します。 例えば、SearchRequest を受けとって SearchResponse を返すメソッドの RPCサービスを定義したいなら、 .proto ファイルには以下のように書きます:
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
プロトコルコンパイラは SearchService という抽象インターフェースと、対応する「スタブ」実装を生成します。 スタブは全ての呼び出しを RpcChannel へ転送します。 そして RpcChanel は抽象インターフェースであり、あなた自身の RPC システム用に定義しなければなりません。 例えば、あなたはメッセージを直列化して HTTP でサーバーへ送る RpcChannel を実装するかもしれません。 言い換えれば、生成されたスタブは、あなたを特定の RPC 実装に縛りつけずに、protocol buffers ベースの RPC呼び出しを作るための型安全なインターフェースを提供します。 ですので、C++ では次のようなコードになるでしょう:
using google::protobuf;
protobuf::RpcChannel* channel;
protobuf::RpcController* controller;
SearchService* service;
SearchRequest request;
SearchResponse response;
void DoSearch() {
// protobuf::RpcChannel と protobuf::RpcController を
// 実装した MyRpcChannel と MyRpcController はあなたが提供します。
channel = new MyRpcChannel("somehost.example.com:1234");
controller = new MyRpcController;
// プロトコルコンパイラは上の定義に従って SearchService クラスを生成します。
service = new SearchService::Stub(channel);
// リクエストを準備します。
request.set_query("protocol buffers");
// RPC を実行します。
service->Search(controller, request, response, protobuf::NewCallback(&Done));
}
void Done() {
delete service;
delete channel;
delete controller;
}
全てのサービスクラスは Service インターフェースも実装します。 Serviceインターフェースは、コンパイル時にメソッドの名前やその入出力型を知らないでも特定のメソッドを呼び出す方法を提供します。 これはサーバー側で、あなたがサービスを登録する RPC サーバーを実装するのに使えます。
using google::protobuf;
class ExampleSearchService : public SearchService {
public:
void Search(protobuf::RpcController* controller,
const SearchRequest* request,
SearchResponse* response,
protobuf::Closure* done) {
if (request->query() == "google") {
response->add_result()->set_url("http://www.google.com");
} else if (request->query() == "protocol buffers") {
response->add_result()->set_url("http://protobuf.googlecode.com");
}
done->Run();
}
};
int main() {
// MyRpcServer はあなたが定義します。
// 特定のインターフェースを実装しなければならないということはありません。
// これあはあくまでも例です。
MyRpcServer server;
protobuf::Service* service = new ExampleSearchService;
server.ExportOnPort(1234, service);
server.Run();
delete service;
return 0;
}
Protocol Buffers 用の RPC 実装を開発するための、進行中のサードパーティプロジェクトがいくつかあります。 我々の知っているプロジェクトへのリンクリストは RPC実装 Wikiページを見てください。
.proto ファイル中の個々の宣言は、いくつかの *options* で注釈付けできます。 オプションは宣言の全体的な意味を変更しませんが、特定のコンテキストでどのように扱われるかに影響します。 利用可能なオプションの完全なリストは、google/protobuf/desriptor.proto で定義されています。
ファイルレベルのオプションがあります。 これは、メッセージ型、列挙型、サービス定義中ではなくて、トップレベルのスコープに書けることを意味します。 メッセージレベルのオプションがあります。 これは、メッセージ定義に書けることを意味します。 フィールド、列挙型、列挙値、サービス型、サービスメソッドに書けるオプションもあります。 しかし今のところは、こういったオプションで有用なものはありません。
option java_package = "com.example.foo";
option java_outer_classname = "Ponycopter";
option optimze_for = SPEED;
message Foo {
option message_set_wire_format = true;
extensions 4 to max;
}
.protoファイルで定義したメッセージ型の Java, Python, C++ コードを生成するには、protoc というプロトコルバッファコンパイラを .proto ファイルにかけます。 まだコンパイラをインストールしていなかったら、パッケージをダウンロードして README に従ってください。
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto
IMPORT_PATH は import 指示で .proto ファイルを検索するディレクトリを指定します。省略された場合は、カレントディレクトリが使われます。複数のインポートディレクトリは、--proto_path オプションを複数回指定することで行えます。検索は指定した順序で行われます。--proto_path の省略形式として -I=IMPORT_PATH を使えます。
--cpp_out は DST_DIR に C++ コードを出力します。詳しくは C++生成コードリファレンスを参照してください。
--java_out は DST_DIR に Java コードを出力します。詳しくは Java生成コードリファレンスを参照してください。
--python_out は DST_DIR に Python コードを出力します。詳しくは Python生成コードリファレンスを参照してください。
一つ以上の .protoファイルを入力しなければなりません。複数の .proto ファイルは一度だけ指定されます。ファイル名はカレントディレクトリの相対パスになりますが、コンパイラがそのフルパス名を決定できるようにするために、それぞれのファイルは IMPORT_PATH に置かれなければなりません。