HINWEIS: Diese Website wurde eingestellt. Die Website wird nach dem 31. Januar 2023 eingestellt. Der Traffic wird auf die neue Website unter https://Protop.dev weitergeleitet. In der Zwischenzeit werden nur Änderungen an aufgeführt.

Von DART generierter Code

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

Auf dieser Seite wird beschrieben, welcher Dart-Code vom Protokollpuffer-Compiler für eine bestimmte Protokolldefinition generiert wird. Unterschiede zwischen dem von proto2 und proto3 generierten Code werden hervorgehoben. Beachten Sie, dass diese Unterschiede im generierten Code auftreten, wie in diesem Dokument beschrieben, und nicht in der Basis-API, die in beiden Versionen gleich sind. Bevor Sie dieses Dokument lesen, sollten Sie den Leitfaden zur Sprache proto2 und/oder den Leitfaden zur Sprache Proto3 lesen.

Compiler-Aufruf

Der Protokollpuffer-Compiler erfordert ein Plug-in, um Dart-Code zu generieren. Bei der Installation gemäß der Anleitung wird ein protoc-gen-dart-Binärprogramm bereitgestellt, das protoc verwendet, wenn es mit dem --dart_out-Befehlszeilen-Flag aufgerufen wird. Das Flag --dart_out teilt dem Compiler mit, wohin die Dart-Quelldateien geschrieben werden. Bei einer .proto-Dateieingabe erzeugt der Compiler unter anderem eine .pb.dart-Datei.

Der Name der Datei .pb.dart wird berechnet, indem der Name der Datei .proto verwendet und zwei Änderungen vorgenommen werden:

  • Die Erweiterung (.proto) wurde durch .pb.dart ersetzt. Eine Datei namens foo.proto führt beispielsweise zu einer Ausgabedatei namens foo.pb.dart.
  • Der Proto-Pfad (mit dem Befehlszeilen-Flag --proto_path oder -I angegeben) wird durch den Ausgabepfad ersetzt, der mit dem Flag --dart_out angegeben wird.

Wenn Sie beispielsweise den Compiler so aufrufen:

protoc --proto_path=src --dart_out=build/gen src/foo.proto src/bar/baz.proto

Der Compiler liest die Dateien src/foo.proto und src/bar/baz.proto. Sie erzeugt Folgendes: build/gen/foo.pb.dart und build/gen/bar/baz.pb.dart. Der Compiler erstellt gegebenenfalls automatisch das Verzeichnis build/gen/bar, erstellt aber nicht build oder build/gen. Diese müssen bereits vorhanden sein.

Nachrichten

Einfache Erklärung:

message Foo {}

Der Protokollpuffer-Compiler generiert eine Klasse mit dem Namen Foo, die die Klasse GeneratedMessage erweitert.

Die Klasse GeneratedMessage definiert Methoden, mit denen Sie die gesamte Nachricht prüfen, bearbeiten, lesen oder schreiben können. Zusätzlich zu diesen Methoden definiert die Klasse Foo die folgenden Methoden und Konstruktoren:

  • Foo(): Standardkonstruktor. Erstellt eine Instanz, in der alle einzelnen Felder nicht festgelegt und wiederkehrende Felder leer sind.
  • Foo.fromBuffer(...): Erstellt eine Foo aus serialisierten Protokollpufferdaten, die die Nachricht darstellen.
  • Foo.fromJson(...): Erstellt ein Foo aus einem JSON-String, der die Nachricht codiert.
  • Foo clone(): Erstellt einen tiefen Klon der Felder in der Nachricht.
  • Foo copyWith(void Function(Foo) updates): Erstellt eine beschreibbare Kopie dieser Nachricht, wendet das updates darauf an und markiert die Kopie vor dem Zurückgeben als schreibgeschützt.
  • static Foo create(): Factory-Funktion zum Erstellen eines einzelnen Foo.
  • static PbList<Foo> createRepeated(): Factory-Funktion zum Erstellen einer Liste, die ein änderbares wiederkehrendes Feld von Foo-Elementen implementiert.
  • static Foo getDefault(): Gibt eine Singleton-Instanz von Foo zurück. Diese Instanz ist mit einer neu erstellten Foo-Instanz identisch. Daher sind alle einzelnen Felder nicht festgelegt und alle wiederkehrenden Felder sind leer.

Verschachtelte Typen

Eine Nachricht kann in einer anderen Nachricht deklariert werden. Beispiel:

message Foo {
  message Bar {
  }
}

In diesem Fall generiert der Compiler zwei Klassen: Foo und Foo_Bar.

Felder

Zusätzlich zu den im vorherigen Abschnitt beschriebenen Methoden generiert der Protokollpufferkompilierer Zugriffsmethoden für jedes Feld, das in der Nachricht in der Datei .proto definiert ist.

Beachten Sie, dass die generierten Namen immer die Camel-Case-Namenskonvention verwenden, auch wenn im Feldnamen in der Datei .proto Kleinbuchstaben (wie empfohlen) verwendet werden. Die Umwandlung von Groß- und Kleinschreibung funktioniert folgendermaßen:

  1. Für jeden Unterstrich im Namen wird der Unterstrich entfernt und der folgende Buchstabe großgeschrieben.
  2. Wenn an den Namen ein Präfix angehängt ist (z.B. „hat“), wird der erste Buchstabe großgeschrieben. Andernfalls wird die Kleinschreibung verwendet.

Für das Feld foo_bar_baz wird also der Getter get fooBarBaz und eine Methode mit dem Präfix has wäre hasFooBarBaz.

Einzelne Primitive Fields (proto2)

Für jede dieser Felddefinitionen gilt:

optional int32 foo = 1;
required int32 foo = 1;

Der Compiler generiert die folgenden Zugriffsmethoden in der Nachrichtenklasse:

  • int get foo: Gibt den aktuellen Wert des Felds zurück. Wenn das Feld nicht festgelegt ist, wird der Standardwert zurückgegeben.
  • bool hasFoo(): Gibt true zurück, wenn das Feld festgelegt ist.
  • set foo(int value): Legt den Wert des Felds fest. Nach dem Aufruf gibt hasFoo() true zurück und get foo gibt value zurück.
  • void clearFoo(): Löscht den Wert des Felds. Nach dem Aufruf gibt hasFoo() den Wert false und get foo den Standardwert zurück.

Für andere einfache Feldtypen wird der entsprechende Dart-Typ entsprechend der Tabelle mit skalaren Werttypen ausgewählt. Bei Nachrichten- und Enum-Typen wird der Werttyp durch die Nachrichten- oder Enum-Klasse ersetzt.

Einzelne Primitive Fields (proto3)

Für diese Felddefinition:

int32 foo = 1;

Der Compiler generiert die folgenden Zugriffsmethoden in der Nachrichtenklasse:

  • int get foo: Gibt den aktuellen Wert des Felds zurück. Wenn das Feld nicht festgelegt ist, wird der Standardwert zurückgegeben.
  • set foo(int value): Legt den Wert des Felds fest. Nach dem Aufruf gibt get foo value zurück.
  • void clearFoo(): Löscht den Wert des Felds. Nach dem Aufruf gibt get foo den Standardwert zurück.

Einzelne Nachrichtenfelder

Für den Nachrichtentyp gilt Folgendes:

message Bar {}
Für eine Nachricht mit dem Feld Bar gilt:
// proto2
message Baz {
  optional Bar bar = 1;
  // The generated code is the same result if required instead of optional.
}

// proto3
message Baz {
  Bar bar = 1;
}

Der Compiler generiert die folgenden Zugriffsmethoden in der Nachrichtenklasse:

  • Bar get bar: Gibt den aktuellen Wert des Felds zurück. Wenn das Feld nicht festgelegt ist, wird der Standardwert zurückgegeben.
  • set bar(Bar value): Legt den Wert des Felds fest. Nach dem Aufruf gibt hasBar() true zurück und get bar gibt value zurück.
  • bool hasBar(): Gibt true zurück, wenn das Feld festgelegt ist.
  • void clearBar(): Löscht den Wert des Felds. Nach dem Aufruf gibt hasBar() den Wert false und get bar den Standardwert zurück.
  • Bar ensureBar(): Setzt bar auf die leere Instanz, wenn hasBar() false zurückgibt, und dann den Wert von bar. Nach dem Aufruf gibt hasBar() true zurück.

Wiederkehrende Felder

Für diese Felddefinition:

repeated int32 foo = 1;

Der Compiler generiert Folgendes:

  • List<int> get foo: Gibt die Liste zurück, die das Feld unterstützt. Wenn das Feld nicht festgelegt ist, wird eine leere Liste zurückgegeben. Änderungen an der Liste werden im Feld angezeigt.

Int64-Felder

Für diese Felddefinition:

// proto2
optional int64 bar = 1;

// proto3
int64 bar = 1;

Der Compiler generiert Folgendes:

  • Int64 get bar: Gibt ein Int64-Objekt zurück, das den Feldwert enthält.

Beachten Sie, dass Int64 nicht in die Dart Core-Bibliotheken eingebunden ist. Für die Arbeit mit diesen Objekten müssen Sie möglicherweise die Dart-Bibliothek fixnum importieren:

import 'package:fixnum/fixnum.dart';

Kartenfelder

Bei einer map-Felddefinition wie dieser:

map<int32, int32> map_field = 1;

Der Compiler generiert den folgenden Getter:

  • Map<int, int> get mapField : Gibt die Dart-Karte zurück, die das Feld unterstützt. Wenn das Feld nicht festgelegt ist, wird eine leere Karte zurückgegeben. Änderungen an der Karte werden im Feld angezeigt.

Alle

Beispiel für ein Any-Feld:

import "google/protobuf/any.proto";

message ErrorStatus {
  string message = 1;
  google.protobuf.Any details = 2;
}

In unserem generierten Code gibt der Getter für das Feld details eine Instanz von com.google.protobuf.Any zurück. Dies bietet die folgenden speziellen Methoden zum Verpacken und Entpacken der Werte von Any:

    /// Unpacks the message in [value] into [instance].
    ///
    /// Throws a [InvalidProtocolBufferException] if [typeUrl] does not correspond
    /// to the type of [instance].
    ///
    /// A typical usage would be `any.unpackInto(new Message())`.
    ///
    /// Returns [instance].
    T unpackInto<T extends GeneratedMessage>(T instance,
        {ExtensionRegistry extensionRegistry = ExtensionRegistry.EMPTY});

    /// Returns `true` if the encoded message matches the type of [instance].
    ///
    /// Can be used with a default instance:
    /// `any.canUnpackInto(Message.getDefault())`
    bool canUnpackInto(GeneratedMessage instance);

    /// Creates a new [Any] encoding [message].
    ///
    /// The [typeUrl] will be [typeUrlPrefix]/`fullName` where `fullName` is
    /// the fully qualified name of the type of [message].
    static Any pack(GeneratedMessage message,
        {String typeUrlPrefix = 'type.googleapis.com'});

Einer

Bei einer oneof-Definition wie dieser:

message Foo {
  oneof test {
    string name = 1;
    SubMessage sub_message = 2;
  }
}

Der Compiler generiert den folgenden Dart-Enum-Typ:

 enum Foo_Test { name, subMessage, notSet }

Außerdem werden die folgenden Methoden generiert:

  • Foo_Test whichTest(): Gibt das Enum zurück, das angibt, welches Feld festgelegt ist. Gibt Foo_Test.notSet zurück, wenn keine festgelegt ist.
  • void clearTest(): Löscht den Wert des einmaligen Felds, das derzeit festgelegt ist (falls vorhanden) und legt den Wert für den Wert Foo_Test.notSet fest.

Für jedes Feld innerhalb der Definition werden die regulären Zugriffsmethoden für Felder generiert. Beispiel für name:

  • String get name: Gibt den aktuellen Wert des Felds zurück, wenn der Wert Foo_Test.name ist. Andernfalls wird der Standardwert zurückgegeben.
  • set name(String value): Legt den Wert des Felds fest und legt den Wert für „Oneof Case“ auf Foo_Test.name fest. Nach dem Aufruf gibt get name value zurück und whichTest() gibt Foo_Test.name zurück.
  • void clearName(): Wenn der Fall nicht Foo_Test.name ist, ändert sich nichts. Andernfalls wird der Wert des Felds gelöscht. Nach dem Aufruf gibt get name den Standardwert und whichTest() den Wert Foo_Test.notSet zurück.

Aufzählungen

Bei einer Enum-Definition wie

enum Color {
  RED = 0;
  GREEN = 1;
  BLUE = 2;
}

Der Protokollpuffer-Compiler generiert eine Klasse mit dem Namen Color, die die Klasse ProtobufEnum erweitert. Die Klasse enthält eine static const Color für jeden der drei definierten Werte sowie eine static const List<Color> mit allen drei Werten. Sie enthält außerdem die folgende Methode:

  • static Color valueOf(int value): Gibt die Color des entsprechenden numerischen Werts zurück.

Jeder Wert hat die folgenden Eigenschaften:

  • name: Der Name des Enum, wie in der .proto-Datei angegeben.
  • value: Der Ganzzahlwert der Aufzählung, wie in der .proto-Datei angegeben.

In der Sprache .proto können mehrere Enum-Symbole denselben numerischen Wert haben. Symbole mit demselben numerischen Wert sind Synonyme. Beispiel:

enum Foo {
  BAR = 0;
  BAZ = 0;
}

In diesem Fall ist BAZ ein Synonym für BAR und wird so definiert:

static const Foo BAZ = BAR;

Ein Enum kann in einem Nachrichtentyp verschachtelt definiert werden. Beispiel: Bei einer Enum-Definition wie

message Bar {
  enum Color {
    RED = 0;
    GREEN = 1;
    BLUE = 2;
  }
}

Der Protokollpuffer-Compiler generiert eine Klasse namens Bar, die GeneratedMessage erweitert, und eine Klasse namens Bar_Color, die ProtobufEnum erweitert.

Erweiterungen (nur proto2)

Bei einer Datei foo_test.proto mit einer Nachricht mit einem Erweiterungsbereich und einer Definition der obersten Ebene:

message Foo {
  extensions 100 to 199;
}

extend Foo {
  optional int32 bar = 101;
}

Der Protokollpuffer-Compiler generiert zusätzlich zur Klasse Foo eine Klasse Foo_test, die ein static Extension für jedes Erweiterungsfeld in der Datei sowie eine Methode zum Registrieren aller Tests in einer ExtensionRegistry enthält:

  • static final Extension bar
  • static void registerAllExtensions(ExtensionRegistry registry): Registriert alle definierten Erweiterungen in der angegebenen Registry.

Die Zugriffsfunktion für Foo von Erweiterungen kann so verwendet werden:

Foo foo = Foo();
foo.setExtension(Foo_test.bar, 1);
assert(foo.hasExtension(Foo_test.bar));
assert(foo.getExtension(Foo_test.bar)) == 1);

Erweiterungen können auch in einer anderen Nachricht verschachtelt sein:

message Baz {
  extend Foo {
    optional int32 bar = 124;
  }
}

In diesem Fall wird die Erweiterung bar stattdessen als statisches Mitglied der Klasse Baz deklariert .

Beim Parsen einer Nachricht, die möglicherweise Erweiterungen enthält, müssen Sie eine ExtensionRegistry angeben, in der Sie alle Erweiterungen registriert haben, die geparst werden sollen. Andernfalls werden diese Erweiterungen wie unbekannte Felder behandelt. Beispiel:

ExtensionRegistry registry = ExtensionRegistry();
registry.add(Baz.bar);
Foo foo = Foo.fromBuffer(input, registry);

Wenn Sie bereits eine geparste Nachricht mit unbekannten Feldern haben, können Sie die Nachricht mit reparseMessage für ExtensionRegistry neu parsen. Wenn die Reihe unbekannter Felder Erweiterungen enthält, die in der Registry vorhanden sind, werden diese Erweiterungen geparst und aus der unbekannten Feldgruppe entfernt. Bereits in der Nachricht vorhandene Erweiterungen werden beibehalten.

Foo foo = Foo.fromBuffer(input);
ExtensionRegistry registry = ExtensionRegistry();
registry.add(Baz.bar);
Foo reparsed = registry.reparseMessage(foo);
Diese Methode zum Abrufen von Erweiterungen ist insgesamt teurer. Nach Möglichkeit empfehlen wir, bei allen erforderlichen Erweiterungen ExtensionRegistry mit GeneratedMessage.fromBuffer zu verwenden.

Dienste

Mit einer Dienstdefinition:

service Foo {
  rpc Bar(FooRequest) returns(FooResponse);
}

Der Protokollpuffer-Compiler kann mit der Option „grpc“ aufgerufen werden (z. B. --dart_out=grpc:output_folder). In diesem Fall generiert er Code zur Unterstützung von gRPC. Weitere Informationen finden Sie in der Kurzanleitung für gRPC Dart.