Protocol Buffer Basics: Dart

denne tutorial giver en grundlæggende Dart programmør introduktion til at arbejde med protocol buffere, ved hjælp af proto3 version af protocol buffere sprog. Ved at gå gennem oprettelse af et simpelt eksempelprogram viser det dig, hvordan du

  • definerer meddelelsesformater i en .proto fil.
  • brug protocol buffer compiler.
  • brug dart protocol buffer API til at skrive og læse meddelelser.

dette er ikke en omfattende guide til brug af protokolbuffere i Dart . For mere detaljerede referenceoplysninger, se Protocol Buffer Language Guide, Dart Language Tour, Dart API Reference, Dart genereret kode Guide og Kodningsreferencen.

Hvorfor bruge protokolbuffere?

det eksempel, vi skal bruge, er et meget simpelt “adressebog” – program, der kan læse og skrive folks kontaktoplysninger til og fra en fil. Hver person i adressebogen har et navn, et ID, en e-mail-adresse og et kontakttelefonnummer.

hvordan serialiserer og henter du strukturerede data som denne? Der er et par måder at løse dette problem på:

  • du kan opfinde en ad hoc måde at kode dataelementerne i en enkelt streng – såsom kodning af 4 ints som “12:3:-23:67”. Dette er en enkel og fleksibel tilgang, selv om det kræver at skrive engangs kodning og parsing kode, og parsing pålægger en lille driftstid omkostninger. Dette fungerer bedst til kodning af meget enkle data.
  • serialiser dataene til KML. Denne tilgang kan være meget attraktiv, da HML er (slags) menneskelig læsbar, og der er bindende biblioteker til mange sprog. Dette kan være et godt valg, hvis du vil dele data med andre applikationer/projekter. Men, er notorisk rumintensiv, og kodning / afkodning det kan pålægge en enorm ydeevne straf på applikationer. Det er også betydeligt mere kompliceret at navigere i et DOM-træ end at navigere i enkle felter i en klasse normalt ville være.

Protokolbuffere er den fleksible, effektive, automatiserede løsning til at løse netop dette problem. Med protokolbuffere skriver du en.proto beskrivelse af den datastruktur, du ønsker at gemme. Fra det opretter protocol buffer compiler en klasse, der implementerer automatisk kodning og parsing af protocol buffer data med et effektivt binært format. Den genererede klasse giver getters og settere for de felter, der udgør en protokolbuffer og tager sig af detaljerne i læsning og skrivning af protokolbufferen som en enhed. Det er vigtigt, at protokolbufferformatet understøtter ideen om at udvide formatet over tid på en sådan måde, at koden stadig kan læse data kodet med det gamle format.

Hvor finder du eksempelkoden

vores eksempel er et sæt kommandolinjeapplikationer til styring af en adressebogdatafil, kodet ved hjælp af protokolbuffere.Kommandoen dart add_person.dart tilføjer en ny post til datafilen. Kommandoen dart list_people.dart analyserer datafilenog udskriver dataene til konsollen.

Du kan finde det komplette eksempel ieksempler directoryaf GitHub repository.

definition af dit protokolformat

for at oprette dit adressebogsprogram skal du starte med en.proto fil. Definitionerne i en.proto fil ersimpelt: du tilføjer en meddelelse for hver datastruktur, du vilserialisere, angiv derefter et navn og en type for hvert felt i meddelelsen. Ivores eksempel er.proto filen, der definerer meddelelserne,addressbook.proto.

.proto filen starter med en pakkedeklaration, som hjælperat forhindre navnekonflikter mellem forskellige projekter.

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

Næste, du har din besked definitioner. En besked er bare en aggregatindeholder et sæt indtastede felter. Mange standard simple datatyper er tilgængelige som felttyper, herunder boolint32floatdouble og string. Du kan også tilføje yderligere struktur til dine meddelelser ved at bruge andre meddelelsestyper som felttyper.

message Person { string name = 1; int32 id = 2; // Unique ID number for this person. string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { string number = 1; PhoneType type = 2; } repeated PhoneNumber phones = 4; google.protobuf.Timestamp last_updated = 5;}// Our address book file is just one of these.message AddressBook { repeated Person people = 1;}

i ovenstående eksempel indeholderPerson meddelelsenPhoneNumber meddelelser, mensAddressBook beskedindeholderPerson meddelelser. Du kan endda definere meddelelsestypernested inde i andre meddelelser-som du kan se, erPhoneNumber typen defineret inde i Person. Du kan også definere enum typer, hvis du vil have et af dine felter til at have enaf en foruddefineret liste over værdier – her vil du angive, at et telefonnummer kan være et af MOBILEHOME, ellerWORK.

markørerne “= 1”, “= 2” på hvert element identificerer det unikke “tag”, som feltet bruger i den binære kodning. Tagnumre 1-15 kræver en mindre byte for at kode end højere tal, så som en optimering kan du beslutte at bruge disse tags til de almindeligt anvendte eller gentagne elementer og efterlade tags 16 og højere for mindre almindeligt anvendte valgfrie elementer. Hvert element i et gentaget felt kræver genkodning af tagnummeret, så gentagne felter er særligt gode kandidater til denne optimering.

hvis en feltværdi ikke er angivet, bruges en standardværdi: nulfor numeriske typer, den tomme streng for strenge, falsk for bools. For indlejrede meddelelser er standardværdien altid” standardinstans “eller” prototype ” af meddelelsen, som ikke har nogen af dens felter indstillet. Hvis du ringer til accessoren for at få værdien af et felt, der ikke er angivet eksplicit, returnerer det altid feltets standardværdi.

Hvis et felt er repeated, kan feltet gentages et vilkårligt antal gange (inklusive nul). Rækkefølgen af de gentagne værdier bevares i protokolbufferen. Tænk på gentagne felter som dynamisk størrelse arrays.

du finder en komplet guide til skrivning.proto filer – inklusive alle mulige felttyper – i Protocol Buffer Language Guide. Gå ikke på udkig efter faciliteter, der ligner klassearv, men protokolbuffere gør det ikke.

kompilering af dine protokolbuffere

nu hvor du har en .proto, er det næste, du skal gøre, at generere de klasser, du skal læse og skrive AddressBook (og dermed Person og PhoneNumber) meddelelser. For at gøre dette skal du køre protocol buffer compiler protoc på din .proto:

  1. hvis du ikke har installeret compileren, skal du hente pakken og følge instruktionerne i README.
  2. installer dart Protocol Buffer plugin som beskrevet i sin README. Den eksekverbare bin/protoc-gen-dart skal være i din PATH for protokolbufferen protoc for at finde den.
  3. Kør nu kompilatoren og specificer kildekoden (hvor din applikations kildekode lever – den aktuelle mappe bruges, hvis du ikke angiver en værdi), destinationsmappen (hvor du vil have den genererede kode til at gå; ofte det samme som $SRC_DIR) og stien til din .proto. I dette tilfælde vil du påberåbe dig:
    protoc -I=$SRC_DIR --dart_out=$DST_DIR $SRC_DIR/addressbook.proto

    fordi du vil have Dart – kode, bruger du --dart_out – lignende muligheder findes for andre understøttede sprog.

dette generereraddressbook.pb.dart i din angivne destinationsmappe.

protokollen Buffer API

generering addressbook.pb.dart giver dig følgende nyttige typer:

  • An AddressBook klasse med en List<Person> get people getter.
  • A Person klasse med accessormetoder til nameidemail og phones.
  • A Person_PhoneNumberklasse, med accessormetoder tilnumberogtype.
  • APerson_PhoneType klasse med statiske felter for hver værdi iPerson.PhoneType enum.

Du kan læse mere om detaljerne i præcis, hvad der genereres i dart Generated Code guide.

skrivning af en besked

lad os nu prøve at bruge dine protokolbufferklasser. Den første ting, du vil have din adressebogsapplikation til at kunne gøre, er at skrive personlige oplysninger til din adressebogsfil. For at gøre dette skal du oprette og udfylde forekomster af dine protokolbufferklasser og derefter skrive dem til en outputstrøm.

Her er et program, der læser en AddressBook fra en fil, tilføjer en ny Person til det baseret på brugerinput, og skriver den nye AddressBook tilbage ud til filen igen. De dele, der direkte kalder eller reference kode genereret af protokollen compiler er fremhævet.

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());}

læsning af en besked

selvfølgelig ville en adressebog ikke være meget brug, hvis du ikke kunne få nogen information ud af det! Dette eksempel læser filen oprettet af ovenstående eksempel og udskriver alle oplysningerne i den.

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);}

udvidelse af en Protokolbuffer

før eller senere efter at du har frigivet koden,der bruger din protokolbuffer, vil du uden tvivl “forbedre” protokolbufferens definition. Hvis du ønsker, at dine nye buffere skal være bagudkompatible, og dine gamle buffere skal være kompatible-og du vil næsten helt sikkert have dette-så er der nogle regler, du skal følge. I den nye version af protocol buffer:

  • du må ikke ændre tagnumrene for eksisterende felter.
  • du kan slette felter.
  • du kan tilføje nye felter, men du skal bruge nye tagnumre (dvs.tagnumre, der aldrig blev brugt i denne protokolbuffer, ikke engang af slettede felter).

(der ernogle undtagelser tildisse regler, men de bruges sjældent.)

Hvis du følger disse regler, vil gammel kode med glæde læse nye meddelelser ogsimpelthen ignorere eventuelle nye felter. Til den gamle kode, ental felter, der varslettet, vil simpelthen have deres standardværdi, og slettede gentagne felter vilvære tomme. Ny kode vil også gennemsigtigt læse gamle meddelelser.

Husk dog, at nye felter ikke vil være til stede i gamle meddelelser,så du bliver nødt til at gøre noget rimeligt med standardværdien. Dentype-specificdefault value bruges: for strenge er standardværdien den tomme streng. For booleans, thedefault værdi er falsk. For numeriske typer er standardværdien nul.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.