Protocol Buffer Basics:Dart

このチュートリアルでは、protocol buffers言語のproto3バージョンを使用して、Protocol buffersを操作するための基本的なDartプログラマの紹介を提供します。 簡単なサンプルアプリケーションを作成することで、

  • メッセージ形式を.protoファイルで定義する方法を示します。
  • プロトコルバッファコンパイラを使用します。
  • メッセージの書き込みと読み取りには、Dartプロトコルバッファー APIを使用します。

これは、Dartでプロトコルバッファーを使用するための包括的なガイドではありません。 より詳細なリファレンス情報については、”Protocol Buffer Language Guide”、”Dart Language Tour”、”Dart API Reference”、”Dart Generated Code Guide”、および”Encoding Reference”を参照してください。

なぜプロトコルバッファーを使用するのですか?使用する例は、ファイルとの間で人々の連絡先の詳細を読み書きできる非常に単純な「アドレス帳」アプリケーションです。 アドレス帳の各ユーザーには、名前、ID、電子メールアドレス、および連絡先の電話番号があります。このような構造化データをシリアル化して取得するにはどうすればよいですか? この問題を解決するにはいくつかの方法があります。

  • データ項目を単一の文字列にエンコードするアドホックな方法を発明することができます。”12:3:-23:67″. これはシンプルで柔軟なアプローチですが、一回限りのエンコードと解析コードを記述する必要があり、解析には小さなランタイムコストがかかります。 これは非常に単純なデータをエンコードするのに最適です。
  • データをXMLにシリアル化します。 XMLは人間が読める(一種の)ものであり、多くの言語用のバインディングライブラリがあるため、このアプローチは非常に魅力的です。 これは、他のアプリケーション/プロジェクトとデータを共有する場合に適しています。 しかし、XMLは悪名高いスペース集約型であり、エンコード/デコードは、アプリケーションに巨大なパフォーマン また、XML DOMツリーをナビゲートすることは、通常のクラス内の単純なフィールドをナビゲートするよりもかなり複雑です。

プロトコルバッファは、この問題を正確に解決するための柔軟で効率的な自動化されたソリューションです。 プロトコルバッファーを使用すると、格納するデータ構造の.proto記述を記述します。 それから、プロトコルバッファコンパイラは、効率的なバイナリ形式でプロトコルバッファーデータの自動エンコードと解析を実装するクラスを作成します。 生成されたクラスは、プロトコルバッファを構成するフィールドのgetterとsetterを提供し、プロトコルバッファを単位として読み書きする詳細を処理します。 重要なのは、プロトコルバッファー形式は、コードが古い形式でエンコードされたデータを読み取ることができるように、時間の経過とともに形式を拡張す

サンプルコードを見つける場所

この例は、プロトコルバッファーを使用してエンコードされたアドレスbookdataファイルを管理するためのコマンコマンドdart add_person.dartdart list_people.dartはデータファイルを解析し、データをコンソールに出力します。完全な例は、GitHubリポジトリのexamples directoryにあります。

プロトコル形式の定義

アドレス帳アプリケーションを作成するには、.proto.proto.protoaddressbook.protoです。

.protoファイルはパッケージ宣言で始まり、異なるプロジェクト間の名前の競合を防ぐのに役立ちます。 p>

syntax = "proto3";package tutorial;import "google/protobuf/timestamp.proto";

次に、メッセージ定義があります。 メッセージは単なる集計です型付きフィールドのセットを含む。 多くの標準単純データ型は、フィールド型として使用できます。boolint32floatdoublestring。 また、他のメッセージタイプをfieldtypesとして使用して、メッセージにさらに構造を追加することもできます。 上記の例では、PersonPhoneNumberAddressBookmessagecontainsPersonPhoneNumberPerson内で定義されています。 ここでは、phonenumberがMOBILEHOMEWORKのいずれかになるように指定します。

各要素の”=1″、”=2″マーカーは、フィールドがバイナリエンコーディングで使用する一意の”タグ”を識別します。 タグ番号1-15は、より高い数値よりもエンコードするために一つの少ないバイトを必要とするので、最適化として、一般的に使用される要素または繰り返 繰り返しフィールド内の各要素はタグ番号を再エンコードする必要があるため、繰り返しフィールドはこの最適化のための特に良い候補です。

フィールド値が設定されていない場合、adefault値が使用されます:数値型の場合はzerofor、文字列の場合は空の文字列、boolの場合はfalse。 Embeddedmessagesの場合、デフォルト値は常にメッセージの”default instance”または”prototype”であり、フィールドは設定されていません。 明示的に設定されていないフィールドの値を取得するためにアクセサーを呼び出すと、常にそのフィールドのデフォルト値が返されます。

フィールドがrepeatedの場合、フィールドは何回でも繰り返すことができます(ゼロを含む)。 繰り返される値の順序はプロトコルバッファに保存されます。 繰り返しフィールドは、動的なサイズの配列と考えてください。

.protoファイルを書くための完全なガイド(可能なすべてのフィールドタイプを含む)は、Protocol Buffer Language Guideにあります。 しかし、クラス継承に似た機能を探しに行かないでください–protocol buffersはそれをしません。これで.protoAddressBookPersonPhoneNumberPhoneNumberPhoneNumberPhoneNumberPhoneNumber/div>)メッセージ。 これを行うには、プロトコルバッファコンパイラprotoc.protoで実行する必要があります。:

  1. コンパイラをインストールしていない場合は、パッケージをダウンロードし、READMEの指示に従ってください。
  2. READMEに記載されているように、Dartプロトコルバッファプラグインをインストールします。 実行可能ファイルbin/protoc-gen-dartPATHprotocそれを見つけるために。
  3. ソースディレクトリ(アプリケーションのソースコードが存在する場所–値を指定しない場合は現在のディレクトリが使用されます)、宛先ディレクトリ(生成されたコードを移動する場所、多くの場合$SRC_DIR.proto
    protoc -I=$SRC_DIR --dart_out=$DST_DIR $SRC_DIR/addressbook.proto

    Dartコードが必要なため、--dart_outaddressbook.pb.dartが生成されます。

    プロトコルバッファAPI

    addressbook.pb.dartを生成すると、次の便利な型が得られます。

    • AddressBookList<Person> get peopleゲッターを持つクラス。
    • Personnameidemailphonesのアクセサメソ
    • Person_PhoneNumbernumbertypeのアクセサメソッドを備えています。
    • aPerson_PhoneType列挙型の各値の静的フィールドを持つクラス。正確に何が生成されるかの詳細については、Dart Generated Code guideを参照してください。

      メッセージを書く

      それでは、プロトコルバッファクラスを使ってみましょう。 あなたのアドレス帳アプリケーションが行うことができるようにしたい最初の事は、あなたのアドレス帳ファイルに個人情報を書くことです。 これを行うには、プロトコルバッファクラスのインスタンスを作成して設定し、それらを出力ストリームに書き込む必要があります。ここでは、ファイルからAddressBookPersonAddressBookをファイルに書き戻 プロトコルコンパイラによって生成されたコードを直接呼び出したり参照したりする部分が強調表示されます。p>

      import 'dart:io';import 'dart_tutorial/addressbook.pb.dart';// This function fills in a Person message based on user input.Person promtForAddress() { Person person = Person(); print('Enter person ID: '); String input = stdin.readLineSync(); person.id = int.parse(input); print('Enter name'); person.name = stdin.readLineSync(); print('Enter email address (blank for none) : '); String email = stdin.readLineSync(); if (email.isNotEmpty) { person.email = email; } while (true) { print('Enter a phone number (or leave blank to finish): '); String number = stdin.readLineSync(); if (number.isEmpty) break; Person_PhoneNumber phoneNumber = Person_PhoneNumber(); phoneNumber.number = number; print('Is this a mobile, home, or work phone? '); String type = stdin.readLineSync(); switch (type) { case 'mobile': phoneNumber.type = Person_PhoneType.MOBILE; break; case 'home': phoneNumber.type = Person_PhoneType.HOME; break; case 'work': phoneNumber.type = Person_PhoneType.WORK; break; default: print('Unknown phone type. Using default.'); } person.phones.add(phoneNumber); } return person;}// Reads the entire address book from a file, adds one person based// on user input, then writes it back out to the same file.main(List arguments) { if (arguments.length != 1) { print('Usage: add_person ADDRESS_BOOK_FILE'); exit(-1); } File file = File(arguments.first); AddressBook addressBook; if (!file.existsSync()) { print('File not found. Creating new file.'); addressBook = AddressBook(); } else { addressBook = AddressBook.fromBuffer(file.readAsBytesSync()); } addressBook.people.add(promtForAddress()); file.writeAsBytes(addressBook.writeToBuffer());}

      メッセージを読む

      もちろん、アドレス帳は、あなたがそれから情報を得ることができなかった場合、あまり使用されません! この例では、上記の例で作成されたファイルを読み取り、その中のすべての情報を出力します。

      import 'dart:io';import 'dart_tutorial/addressbook.pb.dart';import 'dart_tutorial/addressbook.pbenum.dart';// Iterates though all people in the AddressBook and prints info about them.void printAddressBook(AddressBook addressBook) { for (Person person in addressBook.people) { print('Person ID: ${ person.id}'); print(' Name: ${ person.name}'); if (person.hasEmail()) { print(' E-mail address:${ person.email}'); } for (Person_PhoneNumber phoneNumber in person.phones) { switch (phoneNumber.type) { case Person_PhoneType.MOBILE: print(' Mobile phone #: '); break; case Person_PhoneType.HOME: print(' Home phone #: '); break; case Person_PhoneType.WORK: print(' Work phone #: '); break; default: print(' Unknown phone #: '); break; } print(phoneNumber.number); } }}// Reads the entire address book from a file and prints all// the information inside.main(List arguments) { if (arguments.length != 1) { print('Usage: list_person ADDRESS_BOOK_FILE'); exit(-1); } // Read the existing address book. File file = new File(arguments.first); AddressBook addressBook = new AddressBook.fromBuffer(file.readAsBytesSync()); printAddressBook(addressBook);}

      プロトコルバッファを拡張する

      プロトコルバッファを使用するコードをリリースした後、プロトコルバッファの定義を”改善” 新しいバッファを後方互換性にし、古いバッファをbeforward互換性にしたい場合は、ほぼ確実にこれを望んでいます-thenthereには従う必要があるいくつかのルールがあ 新しいバージョンのprotocol buffer:

      • 既存のフィールドのタグ番号を変更してはいけません。
      • フィールドを削除することができます。
      • 新しいフィールドを追加することはできますが、新しいタグ番号を使用する必要があります(つまり、削除されたフィールドでもなく、このプロトコルバッファで使用されなかったタグ番号)。

      (これらのルールにはいくつかの例外がありますが、めったに使用されません。)

      これらのルールに従うと、古いコードは新しいメッセージを喜んで読み取り、新しいフィールドを無視します。 古いコードでは、weredeleted単一フィールドは単にデフォルト値を持ち、削除された繰り返しフィールドは空になります。 新しいコードは古いメッセージも透過的に読み取ります。ただし、古いメッセージには新しいフィールドは存在しないため、デフォルト値で合理的なことを行う必要があることに注意してください。

      しかし、 Atype-specificdefault値が使用されます:文字列の場合、デフォルト値は空の文字列です。 ブール値の場合、デフォルト値はfalseです。 数値型の場合、デフォルト値はゼロです。

コメントを残す

メールアドレスが公開されることはありません。