Go Opaque API: Manuelle Migration
Die Opaque API ist die neueste Version der Protocol Buffers Implementierung für die Programmiersprache Go. Die alte Version wird jetzt Open Struct API genannt. Eine Einführung finden Sie im Blogbeitrag Go Protobuf: Releasing the Opaque API.
Dies ist eine Benutzeranleitung zur Migration von Go Protobuf Verwendungen von der älteren Open Struct API zur neuen Opaque API.
Warnung
Sie betrachten die manuelle Migrationsanleitung. Normalerweise ist es besser, das Toolopen2opaque zur Automatisierung der Migration zu verwenden. Sehen Sie stattdessen Opaque API Migration.Der Leitfaden für generierten Code liefert weitere Details. Diese Anleitung vergleicht die alte und die neue API Seite an Seite.
Nachrichtenkonstruktion
Angenommen, es gibt eine protobuf-Nachricht, die wie folgt definiert ist
message Foo {
uint32 uint32 = 1;
bytes bytes = 2;
oneof union {
string string = 4;
MyMessage message = 5;
}
enum Kind { … };
Kind kind = 9;
}
Hier ist ein Beispiel, wie diese Nachricht aus literalen Werten konstruiert wird
| Open Struct API (alt) | Opaque API (neu) |
| |
Wie Sie sehen, ermöglichen die Builder-Strukturen eine nahezu 1:1-Übersetzung zwischen der Open Struct API (alt) und der Opaque API (neu).
Verwenden Sie im Allgemeinen Builder zur besseren Lesbarkeit. Nur in seltenen Fällen, wie z. B. beim Erstellen von Protobuf-Nachrichten in einer heißen inneren Schleife, kann es vorteilhafter sein, Setter anstelle von Buildern zu verwenden. Weitere Details finden Sie in den Opaque API FAQ: Soll ich Builder oder Setter verwenden?.
Eine Ausnahme zum obigen Beispiel ist die Arbeit mit Oneofs: Die Open Struct API (alt) verwendet für jeden Oneof-Fall einen Wrapper-Strukturtyp, während die Opaque API (neu) Oneof-Felder wie normale Nachrichtenfelder behandelt.
| Open Struct API (alt) | Opaque API (neu) |
| |
Für die Menge der Go-Strukturfelder, die mit einer Oneof-Union verbunden sind, darf nur ein Feld gefüllt werden. Wenn mehrere Oneof-Fallfelder gefüllt sind, gewinnt das letzte (in der Reihenfolge der Felddeklaration in Ihrer .proto-Datei).
Skalare Felder
Angenommen, es gibt eine Nachricht, die mit einem skalaren Feld definiert ist
message Artist {
int32 birth_year = 1;
}
Protobuf-Nachrichtenfelder, für die Go skalare Typen verwendet (bool, int32, int64, uint32, uint64, float32, float64, string, []byte und enum), haben Get- und Set-Zugriffsmethoden. Felder mit expliziter Präsenz haben auch Has- und Clear-Methoden.
Für ein Feld vom Typ int32 namens birth_year werden die folgenden Zugriffsmethoden dafür generiert
func (m *Artist) GetBirthYear() int32
func (m *Artist) SetBirthYear(v int32)
func (m *Artist) HasBirthYear() bool
func (m *Artist) ClearBirthYear()
Get gibt einen Wert für das Feld zurück. Wenn das Feld nicht gesetzt ist oder der Nachrichtenempfänger nil ist, gibt es den Standardwert zurück. Der Standardwert ist der Zero Value, es sei denn, er wird explizit mit der Default-Option gesetzt.
Set speichert den bereitgestellten Wert im Feld. Es führt zu einem Panic, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird.
Bei Byte-Feldern wird das Aufrufen von Set mit einem nil []byte als gesetzt betrachtet. Zum Beispiel gibt das sofortige Aufrufen von Has danach true zurück. Das sofortige Aufrufen von Get danach gibt ein Slice der Länge Null zurück (kann entweder nil oder ein leeres Slice sein). Benutzer sollten Has zur Bestimmung der Präsenz verwenden und sich nicht darauf verlassen, ob Get nil zurückgibt.
Has meldet, ob das Feld gefüllt ist. Es gibt false zurück, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird.
Clear löscht das Feld. Es führt zu einem Panic, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird.
Beispiel-Codeausschnitte mit einem String-Feld in
| Open Struct API (alt) | Opaque API (neu) |
| |
Nachrichtenfelder
Angenommen, es gibt eine Nachricht, die mit einem nachrichtentyp-Feld definiert ist
message Band {}
message Concert {
Band headliner = 1;
}
Protobuf-Nachrichtenfelder vom Typ Nachricht haben Get-, Set-, Has- und Clear-Methoden.
Für ein nachrichtentyp-Feld namens headliner werden die folgenden Zugriffsmethoden dafür generiert
func (m *Concert) GetHeadliner() *Band
func (m *Concert) SetHeadliner(*Band)
func (m *Concert) HasHeadliner() bool
func (m *Concert) ClearHeadliner()
Get gibt einen Wert für das Feld zurück. Es gibt nil zurück, wenn es nicht gesetzt ist oder wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird. Die Prüfung, ob Get nil zurückgibt, ist äquivalent zur Prüfung, ob Has false zurückgibt.
Set speichert den bereitgestellten Wert im Feld. Es führt zu einem Panic, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird. Das Aufrufen von Set mit einem nil-Zeiger ist äquivalent zum Aufrufen von Clear.
Has meldet, ob das Feld gefüllt ist. Es gibt false zurück, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird.
Clear löscht das Feld. Es führt zu einem Panic, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird.
Beispiel-Codeausschnitte
| Open Struct API (alt) | Opaque (neu) |
| |
Wiederholte Felder
Angenommen, es gibt eine Nachricht, die mit einem wiederholten nachrichtentyp-Feld definiert ist
message Concert {
repeated Band support_acts = 2;
}
Wiederholte Felder haben Get- und Set-Methoden.
Get gibt einen Wert für das Feld zurück. Es gibt nil zurück, wenn das Feld nicht gesetzt ist oder der Nachrichtenempfänger nil ist.
Set speichert den bereitgestellten Wert im Feld. Es führt zu einem Panic, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird. Set speichert eine Kopie des Slice-Headers, der bereitgestellt wird. Änderungen am Slice-Inhalt sind im wiederholten Feld sichtbar. Daher gibt das Aufrufen von Get unmittelbar nach dem Aufrufen von Set mit einem leeren Slice denselben Slice zurück. Für die Wire- oder Text-Marshaling-Ausgabe ist ein übergebener nil-Slice von einem leeren Slice nicht zu unterscheiden.
Für ein wiederholtes nachrichtentyp-Feld namens support_acts auf der Nachricht Concert werden die folgenden Zugriffsmethoden dafür generiert
func (m *Concert) GetSupportActs() []*Band
func (m *Concert) SetSupportActs([]*Band)
Beispiel-Codeausschnitte
| Open Struct API (alt) | Opaque API (neu) |
| |
Maps (Zuordnungen)
Angenommen, es gibt eine Nachricht, die mit einem Map-typ-Feld definiert ist
message MerchBooth {
map<string, MerchItems> items = 1;
}
Map-Felder haben Get- und Set-Methoden.
Get gibt einen Wert für das Feld zurück. Es gibt nil zurück, wenn das Feld nicht gesetzt ist oder der Nachrichtenempfänger nil ist.
Set speichert den bereitgestellten Wert im Feld. Es führt zu einem Panic, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird. Set speichert eine Kopie der bereitgestellten Map-Referenz. Änderungen an der bereitgestellten Map sind im Map-Feld sichtbar.
Für ein Map-Feld namens items auf der Nachricht MerchBooth werden die folgenden Zugriffsmethoden dafür generiert
func (m *MerchBooth) GetItems() map[string]*MerchItem
func (m *MerchBooth) SetItems(map[string]*MerchItem)
Beispiel-Codeausschnitte
| Open Struct API (alt) | Opaque API (neu) |
| |
Oneofs
Für jede Oneof-Union-Gruppierung gibt es eine Which-, Has- und Clear-Methode auf der Nachricht. Es gibt auch eine Get-, Set-, Has- und Clear-Methode auf jedem Oneof-Fallfeld in dieser Union.
Angenommen, es gibt eine Nachricht, die mit den Oneof-Feldern image_url und image_data in avatar wie folgt definiert ist
message Profile {
oneof avatar {
string image_url = 1;
bytes image_data = 2;
}
}
Die generierte Opaque API für diesen Oneof wird sein
func (m *Profile) WhichAvatar() case_Profile_Avatar { … }
func (m *Profile) HasAvatar() bool { … }
func (m *Profile) ClearAvatar() { … }
type case_Profile_Avatar protoreflect.FieldNumber
const (
Profile_Avatar_not_set_case case_Profile_Avatar = 0
Profile_ImageUrl_case case_Profile_Avatar = 1
Profile_ImageData_case case_Profile_Avatar = 2
)
Which meldet, welches Fallfeld gesetzt ist, indem es die Feldnummer zurückgibt. Es gibt 0 zurück, wenn keines gesetzt ist oder wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird.
Has meldet, ob eines der Felder innerhalb des Oneof gesetzt ist. Es gibt false zurück, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird.
Clear löscht das aktuell gesetzte Fallfeld im Oneof. Es führt zu einem Panic auf einem nil-Nachrichtenempfänger.
Die generierte Opaque API für jedes Oneof-Fallfeld wird sein
func (m *Profile) GetImageUrl() string { … }
func (m *Profile) GetImageData() []byte { … }
func (m *Profile) SetImageUrl(v string) { … }
func (m *Profile) SetImageData(v []byte) { … }
func (m *Profile) HasImageUrl() bool { … }
func (m *Profile) HasImageData() bool { … }
func (m *Profile) ClearImageUrl() { … }
func (m *Profile) ClearImageData() { … }
Get gibt einen Wert für das Fallfeld zurück. Es gibt den Zero Value zurück, wenn das Fallfeld nicht gesetzt ist oder wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird.
Set speichert den bereitgestellten Wert im Fallfeld. Es löscht auch implizit das zuvor im Oneof-Union gefüllte Fallfeld. Das Aufrufen von Set auf einem Oneof-Nachrichten-Fallfeld mit einem nil-Wert setzt das Feld auf eine leere Nachricht. Es führt zu einem Panic, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird.
Has meldet, ob das Fallfeld gesetzt ist oder nicht. Es gibt false zurück, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird.
Clear löscht das Fallfeld. Wenn es zuvor gesetzt war, wird auch die Oneof-Union gelöscht. Wenn die Oneof-Union auf ein anderes Feld gesetzt ist, wird die Oneof-Union nicht gelöscht. Es führt zu einem Panic, wenn es auf einem nil-Nachrichtenempfänger aufgerufen wird.
Beispiel-Codeausschnitte
| Open Struct API (alt) | Opaque API (neu) |
| |
Reflexion
Code, der das Go reflect-Paket auf Proto-Nachrichtentypen verwendet, um auf Strukturfelder und Tags zuzugreifen, funktioniert bei der Migration weg von der Open Struct API nicht mehr. Der Code muss auf protoreflect migriert werden.
Einige gängige Bibliotheken verwenden Go reflect im Hintergrund, Beispiele sind
- encoding/json
- Verwenden Sie protobuf/encoding/protojson.
- pretty
- cmp
- Um
cmp.Equalordnungsgemäß mit Protobuf-Nachrichten zu verwenden, verwenden Sie protocmp.Transform
- Um