предисловие
Протокольные буферы — это независимый от языка и платформы расширяемый механизм Google для сериализации структурированных данных — по сравнению с XML, но меньше, быстрее и проще. Вы можете определить структурирование своих данных, а затем легко записывать и читать структурированные данные на разных языках в различных потоках данных, используя специально сгенерированный исходный код.
определить тип сообщения
Начнем с очень простого примера. Предположим, вы хотите определить формат сообщения «поискового запроса».Каждый запрос содержит строку запроса, количество страниц, на которых расположены интересующие вас результаты запроса, и количество результатов запроса на страницу. Файл .proto типа сообщения можно определить следующим образом:
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
- Первая строка файла указывает, что вы используете
proto3
Синтаксис: если вы этого не сделаете, компилятор protobuf будет считать, что вы используетеproto2. Это должна быть первая непустая строка файла без комментариев. - сказал
SearchRequest
В определении сообщения указаны три поля (пары имя/значение), по одному для каждой части данных, которые должны быть включены в сообщение этого типа. Каждое поле имеет имя и тип.
Укажите тип поля
В приведенном выше примере все поляСкалярный тип: два целых числа (page_number
иresult_per_page
) и строка (query
). Однако вы также можете указать составные типы для полей, в том числеперечислитьи другие типы сообщений.
Присвоить идентификационный номер
Как и в приведенном выше формате файла, в определении сообщения каждое поле имеет уникальныйчисловой идентификатор. Эти идентификаторы используются в сообщениидвоичный форматПоля, которые идентифицируют каждое поле, не могут быть изменены после его использования. Примечание. Идентификационный номер в пределах [1,15] будет занимать один байт во время кодирования. Идентификационный номер в пределах [16,2047] занимает 2 байта. Следовательно, идентификационные номера в пределах [1,15] должны быть зарезервированы для тех элементов сообщения, которые появляются часто. ВАЖНО: зарезервируйте некоторые идентификаторы для часто встречающихся идентификаторов, которые могут быть добавлены в будущем.
Наименьший идентификационный номер может начинаться с 1 и доходить до 2 ^ 29 - 1, или 536 870 911. Идентификационные номера [19000-19999] использовать нельзя, они зарезервированы при реализации протокола Protobuf. Если вы должны использовать эти зарезервированные идентификационные номера в файле .proto, во время компиляции будет поднято предупреждение.
Укажите правила поля
Поле сообщения может быть одним из следующих:
- Единственное число: правильно сформированное сообщение может содержать ноль или одно (но не более одного) это поле.
-
repeated
: Это поле может повторяться любое количество раз (включая нули) в правильно сформированном сообщении. Порядок повторяющихся значений будет сохранен.
В прото3,repeated
Поля числового типа используются по умолчаниюpacked
кодирование.
packed
ты сможешькодирование буфера протоколаУзнайте больше о кодировании.
Добавить больше типов сообщений
доступны в одном.proto
В файле определены различные типы сообщений. Это полезно, если вы хотите определить несколько связанных сообщений, например, если вы хотите определитьSearchResponse
Формат ответного сообщения, соответствующий типу сообщения, которое может быть добавлено к тому же сообщению.proto
:
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...
}
добавить заметки
быть.proto
Для добавления комментариев к файлам используйте стиль C/C++.//
и/* ... */
грамматика.
/ * SearchRequest表示搜索查询,带有分页选项
*表明响应中包含哪些结果。* /
message SearchRequest {
string query = 1;
int32 page_number = 2; //我们想要哪个页码?
int32 result_per_page = 3; //每页返回的结果数。
}
зарезервированный текст
Если полностью удалить поле или закомментировать еговозобновитьтип сообщения, будущие пользователи могут повторно использовать номер поля при внесении собственных обновлений типа. Это может вызвать серьезные проблемы, если та же самая старая версия будет загружена позже..proto
, включая повреждение данных, ошибки конфиденциальности и т. д. Один из способов убедиться, что этого не происходит, — указать номер поля (и/или имя, что также может вызвать проблемы с сериализацией JSON) удаленного поля.reserved
. Компилятор буфера протокола будет жаловаться, если какие-либо будущие пользователи попытаются использовать эти идентификаторы полей.
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
Обратите внимание, что вы не можетеreserved
Смешайте имена полей и номера полей в операторе.
какая у тебя сборка.proto
?
когда вы бежите поКомпилятор буфера протоколаВремя.proto
, компилятор генерирует код на выбранном вами языке, и вам необходимо использовать типы сообщений, которые вы описываете в файле, включая получение и установку значений полей, сериализацию сообщения в выходной поток и анализ вашего сообщения из входного потока.
- заC ++, компилятор сгенерирует
.h
и.cc
документ.proto
и укажите класс для каждого типа сообщений, описанного в вашем файле. - заJava, компилятор генерирует
.java
файл, содержащий классы для каждого типа сообщений, иBuilder
Специальный класс для создания экземпляров классов сообщений. -
PythonНемного иначе — компилятор Python генерирует модуль, содержащий статические дескрипторы для каждого типа сообщения,
.proto
затем сметаклассИспользуются вместе для создания необходимых классов доступа к данным Python во время выполнения. - заGo, компилятор будет
.pb.go
Для каждого типа сообщения в файле создается один тип файла. - заRuby, компилятор генерирует
.rb
Файл, содержащий модули Ruby для типов сообщений. - заObjective-C, компилятор генерирует
pbobjc.h
иpbobjc.m
документ.proto
, который содержит класс для каждого типа сообщения, описанного в файле. - заС#, компилятор будет
.cs
Создать один файл из каждого файла.proto
, который содержит класс для каждого типа сообщения, описанного в файле.
Вы можете следовать руководству для выбранного вами языка (скоро появится версия proto3), чтобы узнать больше об использовании API для каждого языка. Дополнительные сведения об API см. в соответствующихСправочник по API(версия proto3 скоро).
Тип скалярного значения
Скалярные поля сообщения могут иметь один из следующих типов – в таблице показано.proto
Типы, указанные в файле, и соответствующие типы в автоматически сгенерированных классах:
.proto type | notes | C ++ type | Java type | Python type [2] | Type | Ruby type | C# type | PHP type |
---|---|---|---|---|---|---|---|---|
double | double | double | float | float64 | float | double | float | |
float | float | float | float | FLOAT32 | float | float | float | |
INT32 | Используйте кодировку переменной длины. Неэффективное кодирование отрицательных чисел — если ваше поле может иметь отрицательные значения, используйте вместо этого sint32. | INT32 | INT | INT | INT32 | Fixnum or Bignum (as needed) | INT | Integer |
Int64 | Используйте кодировку переменной длины. Неэффективное кодирование отрицательных чисел — если ваше поле может иметь отрицательные значения, используйте вместо этого sint64. | Int64 | long | int / long [3] | Int64 | TWINS | long | Integer/string[5] |
UINT32 | Используйте кодировку переменной длины. | UINT32 | int [1] | int / long [3] | UINT32 | Fixnum or Bignum (as needed) | UINT | Integer |
UINT64 | Используйте кодировку переменной длины. | UINT64 | Long [1] | int / long [3] | UINT64 | TWINS | ULONG | Integer/string[5] |
SINT32 | Используйте кодировку переменной длины. Целочисленное значение со знаком. Они кодируют отрицательные числа более эффективно, чем обычные int32. | INT32 | INT | INT | INT32 | Fixnum or Bignum (as needed) | INT | Integer |
sint64 | Используйте кодировку переменной длины. Целочисленное значение со знаком. Они кодируют отрицательные числа более эффективно, чем обычные int64. | Int64 | long | int / long [3] | Int64 | TWINS | long | Integer/string[5] |
fixed32 | Всегда четыре байта. Более эффективен, чем uint32, если значение обычно больше 2 28. | UINT32 | int [1] | int / long [3] | UINT32 | Fixnum or Bignum (as needed) | UINT | Integer |
fixed64 | Всегда восемь байт. Более эффективен, чем uint64, если значение обычно больше 256. | UINT64 | Long [1] | int / long [3] | UINT64 | TWINS | ULONG | Integer/string[5] |
sfixed32 | Всегда четыре байта. | INT32 | INT | INT | INT32 | Fixnum or Bignum (as needed) | INT | Integer |
sfixed64 | Всегда восемь байт. | Int64 | long | int / long [3] | Int64 | TWINS | long | Integer/string[5] |
Boolean | Boolean | Boolean | Boolean | Boolean | TrueClass / FalseClass | Boolean | Boolean | |
string | Строки всегда должны содержать текст в кодировке UTF-8 или 7-битный текст ASCII. | string | string | str / unicode[4] | string | String (UTF-8) | string | string |
byte | Может содержать произвольные последовательности байтов. | string | Byte string | Strait | []byte | String (ASCII-8BIT) | Byte string | string |
существуеткодирование буфера протоколаВы можете найти дополнительную информацию о том, как кодировать эти типы при сериализации сообщения.
[1] В Java беззнаковые 32-битные и 64-битные целые числа представляются с использованием их аналогов со знаком, а старший бит просто сохраняется в бите знака.
[2] Во всех случаях установка значения в поле будет выполнять проверку типа, чтобы убедиться, что оно допустимо.
[3] 64-битное или 32-битное целое число без знака всегда представляется как тип long при декодировании, но может быть целым числом, если при установке поля задано целое число. Во всех случаях значение должно соответствовать типу, представленному при установке. См. [2].
[4] Строки Python представляются как Unicode при декодировании, но могут быть str, если задана строка ASCII (это может отличаться).
[5] Integer предназначен для 64-разрядных компьютеров, а String — для 32-разрядных компьютеров.
По умолчанию
При синтаксическом анализе сообщения, если закодированное сообщение не содержит определенного единственного элемента, соответствующее поле в объекте синтаксического анализа будет установлено на значение по умолчанию для этого поля. Эти значения по умолчанию зависят от типа:
- Для строк по умолчанию используется пустая строка.
- Для байтов по умолчанию используются нулевые байты.
- Для логических значений значение по умолчанию равно false.
- Для числовых типов значение по умолчанию равно нулю.
- заперечислить, значение по умолчанию — первоеопределенное значение перечисления,ДолженценностьДолжно быть 0.
- Для полей сообщения это поле не устанавливается. Его точное значение зависит от языка. Для получения дополнительной информации см.Сгенерированный код относится к
Значение по умолчанию для повторяющихся полей пусто (обычно это пустой список для соответствующего языка).
Обратите внимание, что для скалярных полей сообщения после анализа сообщения нет способа определить, задано ли поле явно значение по умолчанию (например, установлено ли логическое значение).false
) или вообще не задавать: это следует учитывать при определении типа сообщения. Например,false
Если вы не хотите, чтобы это поведение происходило по умолчанию, не существует логического значения, которое включает какое-либо поведение, если установлено значение . Также обратите внимание, что если отмеченное поле сообщенияодеялоУстановите значение по умолчанию, которое не будет сериализовано по сети.
Дополнительные сведения о том, как значения по умолчанию работают в сгенерированном коде, см.Руководство по созданию кода.
перечислить
При определении типа сообщения вы можете захотеть, чтобы одно из полей имело только один предопределенный список значений. Например, вы хотите добавитьcorpus
Каждое полеSearchRequest
, где корпус может бытьUNIVERSAL
,WEB
,IMAGES
,LOCAL
,NEWS
,PRODUCTS
илиVIDEO
. Вы можете сделать это очень просто,enum
Добавьте константу для каждого возможного значения, чтобы определить определение сообщения.
В приведенном ниже примере мы добавляемenum
перечислитьCorpus
и поле типаCorpus
:
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
Как вы видете,Corpus
Первая константа перечисления сопоставляется с нулем: каждое перечисление определяетдолженСодержит константу, которая сопоставляется с нулем в качестве первого элемента. Это потому что:
- Должно быть нулевое значение, чтобы мы могли использовать 0 в качестве числаПо умолчанию.
- Нулевое значение должно быть первым элементом, чтобы соответствоватьproto2Семантически совместимый, где первое значение перечисления всегда является значением по умолчанию.
Вы можете определить псевдонимы, указав одно и то же значение для разных констант перечисления. Для этого вам нужноallow_alias
параметры установлены наtrue
, иначе компилятор протокола выдаст сообщение об ошибке, когда найдет псевдоним.
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
enum EnumNotAllowingAlias {
UNKNOWN = 0;
STARTED = 1;
// RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.
}
Константы перечислителя должны находиться в диапазоне 32-битных целых чисел. так какenum
Значения используются онлайнвариантное кодирование, поэтому отрицательные значения неэффективны и поэтому не рекомендуются. ты сможешьenum
Определите s в определении сообщения, как в примере выше,enum
также могут быть определены извне - они могут быть определены в.proto
файл для повторного использования в любых определениях сообщений. Вы также можете использоватьenum
Синтаксис использует тип, объявленный в одном сообщении, как тип поля в другом сообщении.*MessageType*.*EnumType*
когда вы.proto
Время выполнения на компиляторе с использованием буфера протоколаenum
, сгенерированный код будет иметьenum
Соответствующий код на Java или C++, этоEnumDescriptor
— это специальный класс Python, используемый для создания набора символических констант с целочисленными значениями в классе, сгенерированном во время выполнения.
При десериализации в сообщении сохраняются нераспознанные значения перечисления, но при десериализации сообщения способ представления таких значений зависит от языка. В языках, поддерживающих открытые типы перечислений со значениями за пределами указанного символьного диапазона, таких как C++ и Go, неизвестные значения перечислений хранятся только как лежащие в их основе целочисленные представления. В языках с перечислимыми типами перечисления (например, в Java) регистр в перечислениях используется для представления нераспознанных значений, а доступ к базовым целым числам можно получить с помощью специальных методов доступа. В любом случае, если сообщение сериализовано, все равно будут использоваться значения, не распознанные сериализацией сообщения.
о том какenum
Подробнее об использовании сообщений в приложении см.Руководство по созданию кода.
зарезервированное значение
Если полностью удалить запись перечисления или закомментировать еевозобновитьперечисления, значение может быть повторно использовано будущими пользователями при внесении собственных обновлений типа. Это может вызвать серьезные проблемы, если та же самая старая версия будет загружена позже..proto
, включая повреждение данных, ошибки конфиденциальности и т. д. Один из способов предотвратить это — указать числовое значение (и/или имя) удаленной записи, что также может вызвать проблемы с сериализацией JSON.reserved
. Компилятор буфера протокола будет жаловаться, если какие-либо будущие пользователи попытаются использовать эти идентификаторы. ты можешь использовать этоmax
Ключевое слово указывает на сохранение диапазона значений до максимально возможного значения.
enum Foo {
reserved 2, 15, 9 to 11, 40 to max;
reserved "FOO", "BAR";
}
Обратите внимание, что вы не можетеreserved
Смешайте имена полей и значения в инструкции.
Используйте другие типы сообщений
Вы можете использовать другие типы сообщений в качестве типов полей. Например, вы хотите включитьResult
каждого сообщенияSearchResponse
сообщение - для этого вы можете определитьResult
в том же типе сообщения.proto
, затем укажите поле типаResult
серединаSearchResponse
:
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
определение импорта
В приведенном выше примереResult
Типы сообщений определяются в том же файлеSearchResponse
- если тип сообщения, который будет использоваться в качестве типа поля, уже находится в другом.proto
определено в файле, что мне делать?
ты сможешь.proto
пройти черезИмпортироватьиспользовать определения из других файлов. импортировать другие.proto
Определение человека, добавьте оператор импорта вверху файла:
import“myproject / other_protos.proto”;
По умолчанию вы можете использовать только прямой импорт.proto
определение в файле. Однако иногда может потребоваться.proto
Файл перемещается в новое место..proto
Теперь вы можете.proto
поместите фиктивный файл в старое место, чтобы использоватьimport public
Концепция перенаправляет все импортированные данные в новое место вместо того, чтобы напрямую перемещать файлы и обновлять все сайты вызовов за одно изменение.import public
Любой импорт, который содержит этоimport public
Высказывания в прото людей могут транзитивно зависеть от них. Например:
// new.proto
// All definitions are moved here
// old.proto
//This is the proto that all clients are importing.
import public“new.proto”;
import“other.proto”;
// client.proto
import "old.proto";
//您使用old.proto和new.proto中的定义,但不使用other.proto
Компилятор протокола использует-I
/ --proto_path
флаг Искать импортированные файлы в наборе каталогов, указанном в командной строке компилятора протокола. Если флаги не указаны, он будет искать в каталоге, в котором был вызван компилятор. Как правило, вы должны--proto_path
Флаги устанавливаются в корень проекта и используют полные имена для всех импортов.
Использование типов сообщений proto2
можно импортироватьproto2типы сообщений и использовать их в сообщениях proto3 и наоборот. Однако перечисления proto2 нельзя использовать напрямую в синтаксисе proto3 (если импортированные сообщения proto2 используют их, это нормально).
вложенный тип
Вы можете определить и использовать типы сообщений в других типах сообщений, как показано в примере ниже — здесьResult
Сообщение определяется в сообщенииSearchResponse
:
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
Если вы хотите повторно использовать этот тип сообщения за пределами его родительского типа сообщения, вызовите его:*Parent*.*Type*
message SomeOtherMessage {
SearchResponse.Result result = 1;
}
Вы можете вкладывать сообщения так глубоко, как хотите:
message Outer { // Level 0
message MiddleAA { // Level 1
message Inner { // Level 2
int64 ival = 1;
bool booly = 2;
}
}
message MiddleBB { // Level 1
message Inner { // Level 2
int32 ival = 1;
bool booly = 2;
}
}
}
Обновить тип сообщения
Если существующий тип сообщения больше не соответствует всем вашим потребностям — например, вы хотите, чтобы в формате сообщения были дополнительные поля, — но вы все еще хотите использовать код, созданный в старом формате, не волнуйтесь! Обновлять типы сообщений без нарушения существующего кода очень просто. Запомните следующие правила:
- Не изменяйте номера полей любых существующих полей.
- Если добавляется новое поле, любое сообщение, сериализованное с помощью кода, использующего «старый» формат сообщения, все еще может быть проанализировано вновь сгенерированным кодом. Вы должны запомнить эти элементыПо умолчанию, чтобы новый код мог правильно взаимодействовать с сообщениями, созданными старым кодом. Точно так же сообщения, созданные вашим новым кодом, могут быть проанализированы старым кодом: старый двоичный файл просто игнорирует новые поля при разборе. Для получения дополнительной информации см. "Неизвестное поле"часть
- Поля можно удалять, если номер поля больше не используется в обновленном типе сообщения. Вы можете переименовать поле, возможно, добавив префикс «OBSOLETE_» илирезервНомер поля для ваших будущих пользователей
.proto
Этот номер не используется повторно случайно. -
int32
,uint32
,int64
,uint64
,иbool
все совместимы - это означает, что вы можете изменить эти типы на одно поле другого, не нарушая совместимость вперед или назад. Если вы выберете из проводника число, которое не соответствует соответствующему типу, вы получите тот же эффект, что и преобразование числа в этот тип в C++ (например, если вы читаете 64-битное число как int32). , оно будет усечено до 32 бит). -
sint32
иsint64
Совместимы друг с другом, но с другими целочисленными типамиНетсовместимый. -
string``bytes
Они совместимы, если байты действительны в кодировке UTF-8. -
bytes
Встроенные сообщения совместимы, если байты содержат закодированную версию сообщения. -
fixed32
совместим сsfixed32
,иfixed64
использоватьsfixed64
. -
enum
совместим сint32
,uint32
,int64
,иuint64
Термины формата проводов (обратите внимание, что значения будут усечены, если они не подходят). Однако обратите внимание, что при десериализации сообщений клиентский код может обрабатывать их по-разному: например,enum
Нераспознанные типы proto3 будут сохранены в сообщении, но способ представления этого типа при десериализации сообщения зависит от языка. Поля Int всегда сохраняют свое значение. - Измените одно значение нановыйчлен
oneof
безопасен и бинарно совместим.oneof
Если вы уверены, что нет кода для одновременной установки нескольких полей, может быть безопасно переместить несколько полей в новые поля. Переместить любое поле в существующее полеoneof
Не безопасно.
неизвестное поле
Неизвестные поля — это правильно сформированные сериализованные данные буфера протокола, представляющие поля, которые не распознаются синтаксическим анализатором. Например, когда старый двоичный файл анализирует данные, отправленные новым двоичным файлом, с новыми полями, эти новые поля становятся неизвестными полями в старом двоичном файле.
Первоначально сообщения proto3 всегда отбрасывали неизвестные поля во время синтаксического анализа, но в версии 3.5 мы вновь ввели сохранение неизвестных полей, чтобы соответствовать поведению proto2. В версии 3.5 и более поздних неизвестные поля сохраняются во время синтаксического анализа и включаются в сериализованный вывод.
любой
ДолженAny
Тип сообщения, вы можете использовать почту как встроенный тип без необходимости определять свой собственный .proto. ОдинAny
Содержит произвольные сериализованные сообщенияbytes
вместе с URL-адресом, который разрешается в тип сообщения, чтобы действовать как глобальный уникальный идентификатор. использовать этоAny
тип, который вам нуженИмпортироватьgoogle/protobuf/any.proto
.
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}
URL-адрес типа по умолчанию для данного типа сообщения.type.googleapis.com/*packagename*.*messagename*
Реализации различных языков будут поддерживать вспомогательные библиотеки времени выполнения для упаковки и распаковки любого значения типобезопасным способом — например, в Java любой тип будет иметь специальныеpack()
иunpack()
доступ, а в C++ естьPackFrom()
иUnpackTo()
метод:
// Storing an arbitrary message type in Any.
NetworkErrorDetails details = ...;
ErrorStatus status;
status.add_details()->PackFrom(details);
// Reading an arbitrary message from Any.
ErrorStatus status = ...;
for (const Any& detail : status.details()) {
if (detail.Is<NetworkErrorDetails>()) {
NetworkErrorDetails network_error;
detail.UnpackTo(&network_error);
... processing network_error ...
}
}
В настоящее время разрабатывается библиотека времени выполнения для обработки типа Any..
Если вы уже знакомы ссинтаксис прото2, тип Any заменитрасширять.
Oneof
Если у вас есть сообщение со многими полями и одновременно может быть задано не более одного поля, вы можете использовать функцию oneof, чтобы применить это поведение и сэкономить память.
Одно из полей похоже на обычное поле, за исключением того, что все поля находятся в одной общей памяти, и одновременно может быть задано не более одного поля. Установка любого члена oneof автоматически очищает все остальные члены. Вы можете использовать спец.case()
илиWhichOneof()
Метод проверяет, какое значение (если есть) находится в oneof, в зависимости от выбранного вами языка.
Использование одного из
Чтобы определить один из вас, пожалуйста.proto
использоватьoneof
ключевое слово, за которым следует ваше имя, в данном случаеtest_oneof
:
message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}
Затем добавьте поле oneof в определение oneof. Вы можете добавить любой тип поля, но вы не можете использоватьrepeated
поле.
В сгенерированном коде одно из полей имеет те же геттеры и сеттеры, что и обычные поля. Вы также можете использовать специальные методы для проверки значения в oneof (если оно есть). Вы можете найти соответствующиеСправочник по APIНайдите дополнительную информацию об API oneof для выбранного языка.
одна из особенностей
-
Установка поля oneof автоматически очистит все остальные элементы oneof. Поэтому, если вы установите более одного поля, только то, которое вы установили
последний
Поля по-прежнему имеют значения.
SampleMessage message; message.set_name("name"); CHECK(message.has_name()); message.mutable_sub_message(); // Will clear name field. CHECK(!message.has_name());
-
Если синтаксический анализатор встречает несколько членов одного и того же oneof в сети, в анализируемом сообщении используется только последний увиденный элемент.
-
невозможное
repeated
. -
Reflection API работает на одном из полей.
-
Если вы используете C++, убедитесь, что ваш код не приводит к сбою памяти. Следующий пример кода приведет к сбою,
sub_message
позвонив в
set_name()
метод удаляет этот код.SampleMessage message; SubMessage* sub_message = message.mutable_sub_message(); message.set_name("name"); // Will delete sub_message sub_message->set_... // Crashes here
-
Также в C++, если у вас есть
Swap()
Два сообщения с oneofs, каждое сообщение завершается другим результатом сообщения: в приведенном ниже примереmsg1
будет иметьsub_message
,msg2
и будет иметьname
.SampleMessage msg1; msg1.set_name("name"); SampleMessage msg2; msg2.mutable_sub_message(); msg1.swap(&msg2); CHECK(msg1.has_sub_message()); CHECK(msg2.has_name());
проблемы с обратной совместимостью
Будьте осторожны при добавлении или удалении одного из этих полей. Если вы проверите значение, возвращаемое одним изNone
/ NOT_SET
, что может означать, что oneof не был установлен или был установлен в поле в другой версии oneof. Невозможно определить разницу, потому что невозможно узнать, является ли неизвестное поле на проводе одним из членов.
Проблема с повторным использованием ярлыка
- Перемещение полей в или из одного из: После сериализации и разбора сообщения вы можете потерять часть информации (некоторые поля будут очищены). Однако вы можете безопасно перемещать отдельные поля вновыйoneof и может перемещать несколько полей, если известно, что установлено только одно поле.
- удалите поле oneof и добавьте его обратно: Это может очистить текущее установленное поле oneof после сериализации и разбора сообщения.
- разделить или объединить один из: аналогичная проблема с перемещением обычных полей.
карта
Если вы хотите создать ассоциативную карту в определении данных, Protocol Buffers предоставляет удобный синтаксис быстрого доступа:
map < key_type ,value_type > map_field = N ;
...вkey_type
Может быть любого целочисленного или строкового типа (таким образом, любойскаляртипbytes
). Обратите внимание, что перечисления недействительныkey_type
. изvalue_type
Может быть любого типа, кроме другой карты.
Так, например, если вы хотите создать карту проекта, где каждая записьProject
сообщения связаны со строковым ключом, который можно определить следующим образом:
map < string ,Project > projects = 3 ;
- поля карты не могут
repeated
. - Порядок значений карты проводного формата и порядок итерации карты не определены, поэтому вы не можете полагаться на определенный порядок элементов карты.
- При создании текстового формата для
.proto
, карта отсортирована по ключу. Цифровые клавиши отсортированы по номерам. - При синтаксическом анализе или слиянии со строкой, если есть повторяющиеся ключи карты, используется последний увиденный ключ. При синтаксическом анализе карты из текстового формата синтаксический анализ может завершиться ошибкой, если есть повторяющиеся ключи.
- Если поле карты снабжено ключом, но не имеет значения, поведение поля при сериализации зависит от языка. В C++, Java и Python значение типа по умолчанию сериализуется, а в других языках сериализация вообще отсутствует.
API сгенерированной карты в настоящее время доступен для всех языков, поддерживаемых proto3. Вы можете найти соответствующиеСправочник по APIНайдите дополнительную информацию об API Карт на выбранном языке.
Обратная совместимость
Синтаксис сопоставления онлайн эквивалентен следующему, поэтому реализации буфера протокола, которые не поддерживают сопоставление, могут по-прежнему обрабатывать ваши данные:
message MapFieldEntry {
key_type key = 1;
value_type value = 2;
}
repeated MapFieldEntry map_field = N;
Любая реализация буфера протокола, поддерживающая отображение, должна генерировать и принимать данные, приемлемые в соответствии с приведенным выше определением.
Сумка
ты сможешь.proto
добавление файлаpackage
Необязательный спецификатор для предотвращения конфликтов имен между типами протокольных сообщений.
package foo.bar;
message Open { ... }
Затем вы можете использовать спецификатор пакета при определении полей типа сообщения:
message Foo {
...
foo.bar.Open open = 1;
...
}
То, как спецификаторы пакета влияют на сгенерированный код, зависит от выбранного вами языка:
- существуетС++, сгенерированные классы включаются в пространство имен C++. Например,
Open
будет в пространстве именfoo::bar
. - существуетЯва, который используется как пакет Java, если вы
option java_package
существует.proto
Пакет явно указан в документации. - существуетпитон, директива пакета игнорируется, поскольку модули Python организованы в соответствии с их расположением в файловой системе.
- существуетв Го, который используется в качестве имени пакета Go, если вы
option go_package
существует.proto
явно указано в документации. - существуетРубин, сгенерированный класс содержится во вложенном пространстве имен Ruby, преобразованном в желаемую заглавную букву Ruby (первая буква заглавная; если первый символ не является буквой,
PB_
предваряется). Например,Open
будет в пространстве именFoo::Bar
. - существуетС#, пакет преобразуется в PascalCase и используется как пространство имен, если только вы не
option csharp_namespace
существует.proto
явно указано в документации. Например,Open
будет в пространстве именFoo.Bar
.
Пакет и разрешение имени
Разрешение имени типа в языках буфера протокола похоже на C++: сначала просматривается самая внутренняя область, затем следующая область и так далее, каждый пакет считается «внутри» своего родительского пакета. ведущий '. ' (Например,.foo.bar.Baz
) означает начало с самой внешней области.
Компилятор protobuf разрешает импортированные.proto
файл для разрешения всех имен типов. Генератор кода для каждого языка знает, как ссылаться на каждый тип в этом языке, даже если у него разные правила области видимости.
определить услугу
Если вы хотите использовать тип сообщения с системой RPC (удаленный вызов процедур), вы можете.proto
Интерфейс службы RPC определен в файле, и компилятор protobuf сгенерирует код интерфейса службы и заглушки на выбранном вами языке. Так, например, если вы хотите определить метод запроса службы RPC как:SearchRequest
и метод возврата:SearchResponse
,Может.proto
Определите его в файле следующим образом:
service SearchService {
rpc Search(SearchRequest)returns(SearchResponse);
}
Простейшая система RPC для использования с буферами протоколов — этоgRPC: Независимая от платформы система RPC с открытым исходным кодом, разработанная Google. gRPC особенно хорошо работает с protobuf и позволяет.proto
Соответствующий код RPC создается непосредственно в файле с помощью специального подключаемого модуля компилятора protobuf.
Если вы не хотите использовать gRPC, вы также можете использовать protobuf со своей собственной реализацией RPC. ты сможешьВ руководстве по языку Proto2Узнайте больше об этом.
Есть также несколько текущих сторонних проектов, разрабатывающих реализации RPC с использованием протокольных буферов. Для связанного списка элементов, о которых мы знаем, см.Вики-страница сторонних надстроек.
Сопоставление JSON
Proto3 поддерживает каноническое кодирование в JSON, что упрощает обмен данными между системами. Кодировки описаны тип за типом в таблице ниже.
Если в данных, закодированных в формате JSON, отсутствует значениеnull
, или его значение, при синтаксическом анализе в буфер протокола оно будет интерпретировано соответствующим образомПо умолчанию. Если поле имеет значение по умолчанию в буфере протокола, оно по умолчанию будет исключено из данных, закодированных в формате JSON, для экономии места. Реализации могут предоставлять опции для генерации полей со значениями по умолчанию в выходных данных в формате JSON.
proto3 | JSON | Пример JSON | Примечания |
---|---|---|---|
message | object | {"fooBar": v, "g": null,…} |
Сгенерируйте JSON-объект. Имена полей сообщений отображаются в нижнем регистре верблюда и становятся ключами объекта JSON. еслиjson_name Если опция поля указана, указанное значение будет использоваться в качестве ключа. Парсер принимает имена в нижнем регистре (илиjson_name option) и оригинальное имя поля proto.null является допустимым значением для всех типов полей и рассматривается как значение по умолчанию для соответствующего типа поля. |
eunm | String | "FOO_BAR" |
Используйте имя значения перечисления, указанное в proto. Анализатор принимает имена перечислений и целочисленные значения. |
карта | object | {"k": v, …} |
Все ключи преобразуются в строки. |
repeated V. | array | [v, …] |
null Принимается как пустой список[]. |
bool | true,false | true, false |
|
string | string | "Hello World!" |
|
bytes | base64 string | "YWJjMTIzIT8kKiYoKSctPUB+" |
Значение JSON будет представлять собой данные, закодированные в виде строки с использованием стандартной кодировки base64 с дополнением. Принимает стандартную или URL-безопасную кодировку base64 с/без заполнения. |
int32, фиксированный32, uint32 | string | 1, -10, 0 |
Значения JSON будут десятичными числами. Принимает числа или строки. |
int64, фиксированный64, uint64 | string | "1", "-10" |
Значения JSON будут десятичными строками. Принимает числа или строки. |
float,double | number | 1.1, -10.0, 0, "NaN","Infinity" |
Значением JSON будет число или специальное строковое значение «NaN», «Infinity» и «-Infinity». Принимает числа или строки. Экспоненциальное представление также принято. |
any | object | {"@type": "url", "f": v, … } |
Если Any содержит значение со специальным сопоставлением JSON, оно будет преобразовано следующим образом: . В противном случае значение будет преобразовано в объект JSON, а поле будет вставлено для указания фактического типа данных.{"@type": xxx, "value": yyy}``"@type"
|
Timestamp | string | "1972-01-01T10:00:20.021Z" |
Используйте RFC 3339, где результирующий вывод всегда будет нормализован по Z и использует 0, 3, 6 или 9 знаков после запятой. Смещения, отличные от "Z", также принимаются. |
Duration | string | "1.000340012s", "1s" |
Сгенерированный вывод всегда содержит 0, 3, 6 или 9 знаков после запятой, в зависимости от желаемой точности, за которыми следует суффикс "s". Допускаются любые десятичные знаки (и никакие), если они соответствуют точности в наносекундах и требуется суффикс «s». |
Struct | object |
{ … } |
Любой объект JSON. Видеть.struct.proto
|
Wrapper types | various types | 2, "2", "foo", true,"true", null, 0, … |
Оболочка использует то же представление в JSON, что и примитивный тип оболочки, за исключениемnull Представления разрешены и сохраняются при преобразовании и передаче данных. |
FieldMask | string | "f.fooBar,h" |
Видеть.field_mask.proto
|
ListValue | array | [foo, bar, …] |
|
Value | value | любое значение JSON | |
NullValue | null | JSON null |
Параметры JSON
Реализация proto3 JSON может предоставлять следующие параметры:
- Выдать поля со значениями по умолчанию: опущен по умолчанию в выводе proto3 JSONсо значением по умолчаниюполе. Реализации могут предоставить возможность переопределить это поведение и поля вывода с их значениями по умолчанию.
- Игнорировать неизвестные поля: по умолчанию анализатор Proto3 JSON должен отклонять неизвестные поля, но можно предоставить возможность игнорировать неизвестные поля при анализе.
- Используйте прото-имена полей вместо имен нижнего регистра верблюда.: по умолчанию принтер proto3 JSON должен преобразовывать имена полей в нижний регистр и использовать их в качестве имен JSON. Реализации могут предоставлять возможность использовать имена прото-полей в качестве имен JSON. Парсер Proto3 JSON должен принимать преобразованные имена нижнего регистра верблюда и имена полей proto.
- Отправлять значения перечисления как целые числа вместо строк: по умолчанию имя значения перечисления используется в выводе JSON. Могут быть предоставлены опции для использования числового значения значения перечисления.
опции
.proto
Отдельные объявления в файле могут использовать множествоопциикомментировать. Параметры не меняют общего значения объявления, но могут повлиять на то, как оно обрабатывается в определенном контексте. Полный список доступных опций указан вgoogle/protobuf/descriptor.proto
.
Некоторые параметры являются параметрами уровня файла, что означает, что они должны быть записаны в области верхнего уровня, а не в каком-либо сообщении, перечислении или определении службы. Некоторые параметры являются параметрами уровня сообщения, что означает, что они должны быть записаны в определении сообщения. Некоторые параметры являются параметрами уровня поля, что означает, что они должны быть записаны в определении поля. Опции также могут быть записаны для типов перечислений, значений перечислений, типов служб и методов служб, однако в настоящее время полезных опций нет.
Вот некоторые из наиболее распространенных вариантов:
-
java_package
(опция файла): пакет, используемый для сгенерированных классов Java. если.proto
В файле не указаны явные параметрыjava_package
, пакет proto будет использоваться по умолчанию (укажите .proto с помощью ключевого слова «package» в файле). Однако прото-пакеты обычно не создают хороших пакетов Java, потому что прото-пакеты не начинаются с обратного доменного имени. Эта опция не действует, если код Java не сгенерирован.option java_package =“com.example.foo”;
-
java_multiple_files
(параметр файла): вызывает определение сообщений, перечислений и служб верхнего уровня на уровне пакета, а не во внешних классах, названных в честь файла .proto.
option java_multiple_files = true;
-
java_outer_classname
(опция файла): имя класса самого внешнего класса Java (и имя файла), который необходимо сгенерировать. если.proto
в файле не указаноjava_outer_classname
, то, положив.proto
имена файлов преобразуются в верблюжий регистр (поэтомуfoo_bar.proto
статьFooBar.java
) для создания имени класса. Эта опция не действует, если код Java не сгенерирован.
option java_outer_classname =“Ponycopter”;
-
optimize_for
(параметры файла): можно установить
SPEED
,CODE_SIZE
илиLITE_RUNTIME
. Это влияет на генераторы кода C++ и Java (и, возможно, сторонние генераторы) следующим образом:-
SPEED
(по умолчанию): компилятор protobuf будет генерировать код для сериализации, разбора и выполнения других распространенных операций над типами сообщений. Этот код очень оптимизирован. -
CODE_SIZE
: Компилятор protobuf будет генерировать минимальные классы и полагаться на общий код на основе отражения для сериализации, синтаксического анализа и различных других операций. Таким образом, сгенерированный код лучше, чем использованиеSPEED
Гораздо меньше, но операция будет медленнее. Класс по-прежнему будет реализовывать то же самоеSPEED
Схема точно такая же, как и у общедоступного API. Этот режим содержит очень большое количество.proto
Наиболее полезен при применении документов и не требует, чтобы все документы были очень быстрыми. -
LITE_RUNTIME
: Компилятор protobuf сгенерирует библиотеку времени выполнения, которая зависит только от «облегченной» (libprotobuf-lite
вместоlibprotobuf
) тип. Среда выполнения Lite намного меньше (примерно на порядок меньше), чем вся библиотека, но в ней отсутствуют определенные функции, такие как дескрипторы и отражение. Это особенно полезно для приложений, работающих на ограниченных платформах, таких как мобильные телефоны. Компилятор по-прежнему будет выглядеть как вSPEED
Генерирует быстрые реализации всех методов, как в схеме. Сгенерированный класс будет реализовывать толькоMessageLite
Интерфейс для каждого языка, который обеспечивает только полноеMessage
Подмножество методов интерфейса.
option optimize_for = CODE_SIZE;
-
-
cc_enable_arenas
(опция файла): включить для сгенерированного кода C++Распределение арены. -
objc_class_prefix
(параметры файла): устанавливает префикс класса Objective-C, который добавляется ко всем сгенерированным классам и перечислениям Objective-C для этого .proto. Там нет значения по умолчанию. вы должны использоватьApple предложилПрефикс из 3-5 символов верхнего регистра. Обратите внимание, что Apple резервирует все двухбуквенные префиксы. -
deprecated
(параметры поля): если установленоtrue
, это поле устарело и не должно использоваться в новом коде. В большинстве языков это не имеет практического значения. В Java это становится@Deprecated
Примечания. В будущем генераторы кода для других языков могут генерировать устаревшие комментарии к методам доступа к полю, что вызовет предупреждение при компиляции кода, пытающегося использовать это поле. Если поле никем не используется и вы хотите запретить его использование новыми пользователями, рассмотрите возможность замены объявления поля зарезервированным оператором.int32 old_field = 6 [deprecated = true];
пользовательские параметры
Буферы протоколов также позволяют вам определять и использовать свои собственные параметры. Это то, что большинству людей не нужноРасширенные возможности. Если вы считаете, что вам нужно создать свои собственные параметры, см.Руководство по языку Proto2для деталей. Обратите внимание, что для создания пользовательских параметров используетсяимя расширенияРазрешено только для пользовательских параметров в proto3.
создать свой класс
В соответствии с фактическими рабочими потребностями создавайте собственные типы сообщений на следующих языках: Java, Python, C++, Go, Ruby, Objective-C или C#..proto
файл, вам нужно запустить компилятор protobufprotoc
начальство.proto
. Если компилятор еще не установлен, пожалуйстаскачать пакети следуйте инструкциям в файле readme. Для Go также необходимо установить специальный плагин генератора кода для компилятора: его можно найти на GitHub.golang / protobufНайдите это и инструкции по установке в репозитории.
Компилятор Protobuf вызывается следующим образом:
protoc --proto_path = IMPORT_PATH --cpp_out = DST_DIR --java_out = DST_DIR --python_out = DST_DIR --go_out = DST_DIR --ruby_out = DST_DIR --objc_out = DST_DIR --csharp_out = DST_DIR path / to / file .proto
-
IMPORT_PATH
уточнить.proto
Разобратьimport
Каталог, в котором искать файлы при директиве. Если опущено, используется текущий каталог. в состоянии пройти--proto_path
Передайте параметр несколько раз, чтобы указать несколько каталогов импорта; они будут искаться по порядку. Может использоваться как краткая форма.-I=*IMPORT_PATH*``--proto_path
-
Вы можете указать одну или несколько выходных директив:
-
--cpp_out
Сгенерировать код C++DST_DIR
. Для получения дополнительной информации см.Справочник по сгенерированному коду C++. -
--java_out
Сгенерировать Java-кодDST_DIR
. видетьСправочник по сгенерированному коду JavaБолее. -
--python_out
Сгенерировать код PythonDST_DIR
. ВидетьСправочник по сгенерированному Python кодуБолее. -
--go_out
Сгенерировать код GoDST_DIR
. Для получения дополнительной информации см.Ссылка на сгенерированный код Go. -
--ruby_out
Сгенерировать код RubyDST_DIR
. Справочник по сгенерированному Ruby коду скоро появится! -
--objc_out
Сгенерировать код цели-CDST_DIR
. Для получения дополнительной информации см.Справочник по коду, сгенерированному в Objective-C. -
--csharp_out
Сгенерировать код C#DST_DIR
. Для получения дополнительной информации см.Справочник по сгенерированному C# коду. -
--php_out
Сгенерировать PHP-кодDST_DIR
. ВидетьСправочник по сгенерированному коду PHPБолее.
Для удобства, если DST_DIR заканчивается на
.zip
или.jar
, компилятор запишет вывод в один архивный файл формата ZIP с заданным именем..jar
На выходе также будет предоставлен файл манифеста, как того требует спецификация Java JAR. Учтите, что если выходной архив уже существует, он будет перезаписан, компилятор не настолько умен, чтобы добавлять файлы в существующий архив. -
-
Вы должны предоставить один или несколько
.proto
файл в качестве входных данных..proto
Несколько файлов могут быть указаны одновременно. Хотя имена файлов относятся к текущему каталогу, каждый файл должен находиться в одном из файлов,IMPORT_PATH
чтобы компилятор мог определить его каноническое имя.