Opaque API: Ручная миграция
Описывает ручную миграцию на Opaque API
Opaque API — это последняя версия реализации Protocol Buffers для языка программирования Go. Старая версия теперь называется Open Struct API. Смотрите Go Protobuf: Releasing the Opaque API (блогпост) для введения.
Это руководство пользователя по миграции использования Go Protobuf со старого Open Struct API на новый Opaque API.
{{% alert title="Предупреждение" color="warning" %}} Вы
смотрите руководство по ручной миграции. Обычно лучше использовать
инструмент open2opaque для автоматизации миграции. Смотрите
Миграция на Opaque API
вместо этого. {{% /alert %}}
Руководство по сгенерированному коду предоставляет более подробную информацию. Это руководство сравнивает старый и новый API параллельно.
Создание сообщений
Предположим, есть сообщение protobuf, определенное следующим образом:
message Foo {
uint32 uint32 = 1;
bytes bytes = 2;
oneof union {
string string = 4;
MyMessage message = 5;
}
enum Kind { … };
Kind kind = 9;
}
Вот пример того, как создать это сообщение из литеральных значений:
| Open Struct API (старый) | Opaque API (новый) |
|
|
Как вы можете видеть, структуры builder позволяют осуществить почти 1:1 перевод между Open Struct API (старый) и Opaque API (новый).
В целом, предпочтительнее использовать builders для удобочитаемости. Только в редких случаях, например, при создании сообщений Protobuf в горячем внутреннем цикле, может быть предпочтительнее использовать сеттеры вместо builders. Смотрите Opaque API FAQ: Следует ли использовать builders или сеттеры? для более подробной информации.
Исключением из приведенного выше примера является работа с oneof: Open Struct API (старый) использует тип структуры-обертки для каждого случая oneof, тогда как Opaque API (новый) рассматривает поля oneof как обычные поля сообщения:
| Open Struct API (старый) | Opaque API (новый) |
|
|
Для набора полей структуры Go, связанных с объединением oneof, только одно поле может быть заполнено. Если заполнено несколько полей случаев oneof, побеждает последнее (в порядке объявления полей в вашем .proto файле).
Скалярные поля
Предположим, есть сообщение, определенное со скалярным полем:
message Artist {
int32 birth_year = 1;
}
Поля сообщений Protobuf, для которых Go использует скалярные типы (bool, int32, int64,
uint32, uint64, float32, float64, string, []byte и enum), будут иметь методы доступа Get и
Set. Поля с
явным присутствием
также будут иметь методы Has и Clear.
Для поля типа int32 с именем birth_year будут сгенерированы следующие методы доступа
для него:
func (m *Artist) GetBirthYear() int32
func (m *Artist) SetBirthYear(v int32)
func (m *Artist) HasBirthYear() bool
func (m *Artist) ClearBirthYear()
Get возвращает значение для поля. Если поле не установлено или получатель сообщения
равен nil, он возвращает значение по умолчанию. Значение по умолчанию —
нулевое значение, если явно не установлено с
помощью опции default.
Set сохраняет предоставленное значение в поле. Он вызывает панику при вызове на nil
получателе сообщения.
Для полей bytes вызов Set с nil []byte будет считаться установленным. Например,
вызов Has сразу после возвращает true. Вызов Get сразу
после вернет срез нулевой длины (может быть либо nil, либо пустым срезом). Пользователи
должны использовать Has для определения присутствия и не полагаться на то, возвращает ли Get
nil.
Has сообщает, заполнено ли поле. Он возвращает false при вызове на
nil получателе сообщения.
Clear очищает поле. Он вызывает панику при вызове на nil получателе сообщения.
Примеры фрагментов кода, использующих поле string в:
| Open Struct API (старый) | Opaque API (новый) |
|
|
Поля сообщений
Предположим, есть сообщение, определенное с полем типа сообщение:
message Band {}
message Concert {
Band headliner = 1;
}
Поля сообщений Protobuf типа message будут иметь методы Get, Set, Has и
Clear.
Для поля типа message с именем headliner будут сгенерированы следующие методы доступа
для него:
func (m *Concert) GetHeadliner() *Band
func (m *Concert) SetHeadliner(*Band)
func (m *Concert) HasHeadliner() bool
func (m *Concert) ClearHeadliner()
Get возвращает значение для поля. Он возвращает nil, если не установлено или при вызове на
nil получателе сообщения. Проверка, возвращает ли Get nil, эквивалентна проверке
возвращает ли Has false.
Set сохраняет предоставленное значение в поле. Он вызывает панику при вызове на nil
получателе сообщения. Вызов Set с nil указателем эквивалентен вызову
Clear.
Has сообщает, заполнено ли поле. Он возвращает false при вызове на
nil получателе сообщения.
Clear очищает поле. Он вызывает панику при вызове на nil получателе сообщения.
Примеры фрагментов кода
| Open Struct API (старый) | Opaque (новый) |
|
|
Повторяющиеся поля
Предположим, есть сообщение, определенное с повторяющимся полем типа сообщение:
message Concert {
repeated Band support_acts = 2;
}
Повторяющиеся поля будут иметь методы Get и Set.
Get возвращает значение для поля. Он возвращает nil, если поле не установлено или
получатель сообщения равен nil.
Set сохраняет предоставленное значение в поле. Он вызывает панику при вызове на nil
получателе сообщения. Set сохранит копию заголовка среза, который предоставлен.
Изменения в содержимом среза наблюдаемы в повторяющемся поле. Следовательно, если
Set вызывается с пустым срезом, вызов Get сразу после вернет
тот же срез. Для вывода wire или text маршалинга переданный nil срез
неотличим от пустого среза.
Для повторяющегося поля типа message с именем support_acts в сообщении Concert,
будут сгенерированы следующие методы доступа для него:
func (m *Concert) GetSupportActs() []*Band
func (m *Concert) SetSupportActs([]*Band)
Примеры фрагментов кода
| Open Struct API (старый) | Opaque API (новый) |
|
|
Отображения
Предположим, есть сообщение, определенное с полем типа отображение:
message MerchBooth {
map<string, MerchItems> items = 1;
}
Поля-отображения будут иметь методы Get и Set.
Get возвращает значение для поля. Он возвращает nil, если поле не установлено или
получатель сообщения равен nil.
Set сохраняет предоставленное значение в поле. Он вызывает панику при вызове на nil
получателе сообщения. Set сохранит копию предоставленной ссылки на отображение. Изменения
в предоставленном отображении наблюдаемы в поле отображения.
Для поля-отображения с именем items в сообщении MerchBooth будут сгенерированы следующие методы доступа
для него:
func (m *MerchBooth) GetItems() map[string]*MerchItem
func (m *MerchBooth) SetItems(map[string]*MerchItem)
Примеры фрагментов кода
| Open Struct API (старый) | Opaque API (новый) |
|
|
Oneofs
Для каждой группы объединения oneof будут методы Which, Has и Clear
в сообщении. Также будут методы Get, Set, Has и Clear для
каждого поля случая oneof в этом объединении.
Предположим, есть сообщение, определенное с полями oneof image_url и
image_data в oneof avatar следующим образом:
message Profile {
oneof avatar {
string image_url = 1;
bytes image_data = 2;
}
}
Сгенерированный Opaque API для этого oneof будет:
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 сообщает, какое поле случая установлено, возвращая номер поля. Он
возвращает 0, когда ни одно не установлено или при вызове на nil получателе сообщения.
Has сообщает, установлено ли любое из полей внутри oneof. Он возвращает
false при вызове на nil получателе сообщения.
Clear очищает текущее установленное поле случая в oneof. Он вызывает панику на nil
получателе сообщения.
Сгенерированный Opaque API для каждого поля случая oneof будет:
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 возвращает значение для поля случая. Он вернет нулевое значение, если
поле случая не установлено или при вызове на nil получателе сообщения.
Set сохраняет предоставленное значение в поле случая. Он также неявно очищает
поле случая, которое было ранее заполнено в объединении oneof. Вызов
Set на поле случая сообщения oneof с nil значением установит поле в
пустое сообщение. Он вызывает панику при вызове на nil получателе сообщения.
Has сообщает, установлено ли поле случая или нет. Он возвращает false при вызове
на nil получателе сообщения.
Clear очищает поле случая. Если оно было ранее установлено, объединение oneof также
очищается. Если объединение oneof установлено на другое поле, оно не очистит
объединение oneof. Он вызывает панику при вызове на nil получателе сообщения.
Примеры фрагментов кода
| Open Struct API (старый) | Opaque API (новый) |
|
|
Рефлексия
Код, который использует пакет Go reflect на типах сообщений proto для доступа к полям структуры
и тегам, больше не будет работать при миграции с Open Struct
API. Коду потребуется перейти на использование
protoreflect
Некоторые распространенные библиотеки используют Go reflect под капотом, примеры:
- encoding/json
- Используйте protobuf/encoding/protojson.
- pretty
- cmp
- Чтобы правильно использовать
cmp.Equalс сообщениями protobuf, используйте protocmp.Transform
- Чтобы правильно использовать