Deze tutorial biedt een basis Dart programmeur ‘ s inleiding tot het werken met protocol buffers, met behulp van de proto3 versie van de protocol buffers taal. Door een eenvoudige voorbeeldtoepassing te doorlopen, laat het je zien hoe je
- berichtindelingen definieert in een
.proto
bestand. - gebruik de protocolbuffercompiler.
- gebruik de dart protocol buffer API om berichten te schrijven en te lezen.
Dit is geen uitgebreide handleiding voor het gebruik van protocol buffers in Dart . Voor meer gedetailleerde referentie-informatie, zie de Protocol Buffer taal Gids, De Dart taal Tour, de dart API referentie, de Dart gegenereerde Code gids, en de codering referentie.
waarom protocol buffers gebruiken?
het voorbeeld dat we gaan gebruiken is een zeer eenvoudige” adresboek ” applicatie die de contactgegevens van mensen van en naar een bestand kan lezen en schrijven. Elke persoon in het adresboek heeft een naam, een ID, een e-mailadres en een telefoonnummer.
hoe serialiseer en haal je gestructureerde gegevens op zoals deze? Er zijn een paar manieren om dit probleem op te lossen:
- u kunt een ad-hoc manier bedenken om de gegevensitems in een enkele tekenreeks te coderen-zoals het coderen van 4 ints als “12:3:-23:67”. Dit is een eenvoudige en flexibele aanpak, hoewel het vereist het schrijven van eenmalige codering en parsing code, en de parsing legt een kleine run-time kosten. Dit werkt het beste voor het coderen van zeer eenvoudige gegevens.
- Serialiseer de gegevens naar XML. Deze aanpak kan erg aantrekkelijk zijn omdat XML (een soort van) leesbaar is voor mensen en er bindende bibliotheken zijn voor veel talen. Dit kan een goede keuze zijn als u gegevens wilt delen met andere toepassingen/projecten. Echter, XML is notoir ruimte-intensief, en coderen/decoderen het kan een enorme prestatie boete op te leggen aan toepassingen. Ook is het navigeren in een XML DOM-boom aanzienlijk ingewikkelder dan het navigeren in eenvoudige velden in een klasse normaal zou zijn.
Protocolbuffers zijn de flexibele, efficiënte, geautomatiseerde oplossing om precies dit probleem op te lossen. Met protocolbuffers schrijf je een .proto
beschrijving van de gegevensstructuur die je wilt opslaan. Vanuit dat, de protocol buffer compiler creëert een klasse die automatische codering en parsing van de protocol buffer gegevens implementeert met een efficiënt binair formaat. De gegenereerde klasse biedt getters en setters voor de velden die een protocol buffer vormen en zorgt voor de details van het lezen en schrijven van de protocol buffer als een eenheid. Belangrijk is dat het protocol buffer formaat het idee ondersteunt om het formaat in de tijd uit te breiden, zodat de code nog steeds gegevens kan lezen die gecodeerd zijn met het oude formaat.
waar de voorbeeldcode te vinden is
ons voorbeeld is een set command-lineapplicaties voor het beheren van een adresboekdatabestand, gecodeerd met protocolbuffers.Het commando dart add_person.dart
voegt een nieuwe regel toe aan het databestand. Het commando dart list_people.dart
ontleedt het gegevensbestand en drukt de gegevens af naar de console.
je kunt het volledige voorbeeld vinden in de examples directory van de GitHub repository.
Defining your protocol format
om uw adresboektoepassing aan te maken, moet u beginnen met een.proto
bestand. De definities in een .proto
bestand zijn eenvoudig: u voegt een bericht toe voor elke gegevensstructuur die u wilt serialiseren en specificeert vervolgens een naam en een type voor elk veld in het bericht. In ons voorbeeld is het.proto
bestand dat de berichten definieertaddressbook.proto
.
het .proto
bestand begint met een pakketdeclaratie, die helpt om naamgevingsconflicten tussen verschillende projecten te voorkomen.
syntax = "proto3";package tutorial;import "google/protobuf/timestamp.proto";
vervolgens heeft u uw berichtdefinities. Een bericht is gewoon een aggregaat met een set getypte velden. Veel standaardgegevenstypen zijn beschikbaar als veldtypen, waaronder bool
int32
float
double
, en string
. U kunt ook verdere structuur toevoegen aan uw berichten door andere berichttypes als veldtypes te gebruiken.
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;}
in het bovenstaande voorbeeld bevat het Person
berichtPhoneNumber
berichten, terwijl de AddressBook
messagebevat Person
berichten. U kunt zelfs message typesnest in andere berichten-zoals u kunt zien, dePhoneNumber
type is gedefinieerd in Person
. U kunt ook enum
typen definiëren als u wilt dat een van uw velden een van een vooraf gedefinieerde lijst met waarden heeft – hier wilt u opgeven dat een telefoonnummer een van MOBILE
HOME
, ofWORK
kan zijn.
de” = 1″,” = 2 “markers op elk element identificeren de unieke” tag ” die het veld gebruikt in de binaire codering. Tagnummers 1-15 vereisen een byte minder om te coderen dan hogere getallen, dus als optimalisatie kunt u besluiten om deze tags te gebruiken voor de veelgebruikte of herhaalde elementen, waarbij tags 16 en hoger worden gelaten voor minder algemeen gebruikte optionele elementen. Elk element in een herhaald veld vereist het opnieuw coderen van het tagnummer, dus herhaalde velden zijn bijzonder goede kandidaten voor deze optimalisatie.
als een veldwaarde niet is ingesteld, wordt een standaardwaarde gebruikt: zerofor numerieke types, de lege string voor strings, false Voor bools. Voor embeddedmessages is de standaardwaarde altijd de ” standaard instantie “of” prototype ” van het bericht, dat geen van zijn Velden heeft ingesteld. Het aanroepen van de accessor om de waarde te krijgen van een veld dat niet expliciet is ingesteld, geeft altijd de standaardwaarde van dat veld terug.
als een veld repeated
is, mag het veld een willekeurig aantal keren worden herhaald (inclusief nul). De volgorde van de herhaalde waarden wordt bewaard in de protocolbuffer. Zie herhaalde velden als dynamische arrays.
u vindt een complete handleiding voor het schrijven van .proto
bestanden – inclusief alle mogelijke veldtypen – in de protocol Buffer Language Guide. Ga niet op zoek naar faciliteiten vergelijkbaar met klasse erfenis, hoewel-protocol buffers doen dat niet.
compileren van uw protocolbuffers
nu u een .proto
hebt, is het volgende wat u moet doen het genereren van de klassen die u nodig hebt om te lezen en te schrijven AddressBook
(en dus Person
en PhoneNumber
) berichten. Om dit te doen, moet je de protocol buffer compiler protoc
uitvoeren op je .proto
:
- als je de compiler niet hebt geïnstalleerd, download dan het pakket en volg de instructies in de README.
- installeer de dart Protocol Buffer plugin zoals beschreven in zijn README. Het uitvoerbare
bin/protoc-gen-dart
moet in uwPATH
staan voor de protocolbufferprotoc
om het te vinden. - Voer nu de compiler uit, waarbij u de bronmap opgeeft (waar de broncode van uw toepassing zich bevindt – de huidige map wordt gebruikt als u geen waarde opgeeft), de doelmap (waar u de gegenereerde code heen wilt; vaak hetzelfde als
$SRC_DIR
), en het pad naar uw.proto
. In dit geval zou u:protoc -I=$SRC_DIR --dart_out=$DST_DIR $SRC_DIR/addressbook.proto
aanroepen omdat u Dart – code wilt, gebruikt u de optie
--dart_out
– soortgelijke opties zijn beschikbaar voor andere ondersteunde talen.
dit genereert addressbook.pb.dart
in uw opgegeven doelmap.
de Protocolbuffer API
genereren addressbook.pb.dart
geeft u de volgende nuttige types:
- An
AddressBook
klasse met eenList<Person> get people
getter. - A
Person
klasse met accessormethoden voorname
id
email
enphones
. - A
Person_PhoneNumber
klasse, met accessormethoden voornumber
entype
. - A
Person_PhoneType
klasse met statische velden voor elke waarde in hetPerson.PhoneType
enum.
u kunt meer lezen over de details van wat er precies wordt gegenereerd in de dart Generated Code guide.
het schrijven van een bericht
laten we nu proberen uw protocol buffer klassen te gebruiken. Het eerste wat u wilt dat uw adresboek applicatie te kunnen doen is het schrijven van persoonlijke gegevens aan uw adresboek bestand. Om dit te doen, moet je instanties van je protocol buffer klassen aanmaken en bevolken en ze vervolgens naar een uitvoerstream schrijven.
Hier is een programma dat een AddressBook
uit een bestand leest, er een nieuwe Person
aan toevoegt op basis van gebruikersinvoer, en de nieuwe AddressBook
weer naar het bestand schrijft. De delen die direct aanroep-of referentiecode genereren door de protocolcompiler worden gemarkeerd.
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());}
het lezen van een bericht
natuurlijk zou een adresboek niet veel nut hebben als je er geen informatie uit kon halen! Dit voorbeeld leest het bestand gemaakt door het bovenstaande voorbeeld en drukt alle informatie in het.
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);}
een Protocolbuffer uitbreiden
vroeg of laat nadat u de code hebt vrijgegeven die uw protocolbuffer gebruikt,zult u ongetwijfeld de definitie van de protocolbuffer willen” verbeteren”. Als je wilt dat je nieuwe buffers achterwaarts compatibel zijn, en je oude buffers Forward-compatibel zijn – en je wilt dit bijna zeker – dan zijn er enkele regels die je moet volgen. In de nieuwe versie van de protocolbuffer:
- mag u de tagnummers van bestaande velden niet wijzigen.
- u kunt velden verwijderen.
- u kunt nieuwe velden toevoegen, maar u moet verse tagnummers gebruiken (dat wil zeggen tagnummers die nooit in deze protocolbuffer zijn gebruikt, zelfs niet door verwijderde velden).
(Er zijn enkele uitzonderingen op deze regels, maar ze worden zelden gebruikt.)
Als u deze regels volgt, zal oude code graag nieuwe berichten lezen en eenvoudig nieuwe velden negeren. Voor de oude code hebben enkelvoudsvelden die zijn verwijderd gewoon hun standaardwaarde, en verwijderde herhaalde velden zullen leeg zijn. Nieuwe code zal ook transparant oude berichten lezen.
Houd er echter rekening mee dat nieuwe velden niet aanwezig zullen zijn in oude berichten,dus u zult iets redelijks moeten doen met de standaardwaarde. Atype-specifieke standaardwaarde wordt gebruikt: voor tekenreeksen is de standaardwaarde de lege tekenreeks. Voor booleans is de standaardwaarde onwaar. Voor numerieke typen is de standaardwaarde nul.