Protocol Buffers 2023 版語言規範
該語法使用擴充套件巴科斯-瑙爾正規化 (EBNF) 進行規定。
| alternation
() grouping
[] option (zero or one time)
{} repetition (any number of times)
詞法元素
字母和數字
letter = "A" ... "Z" | "a" ... "z"
capitalLetter = "A" ... "Z"
decimalDigit = "0" ... "9"
octalDigit = "0" ... "7"
hexDigit = "0" ... "9" | "A" ... "F" | "a" ... "f"
識別符號
ident = letter { letter | decimalDigit | "_" }
fullIdent = ident { "." ident }
messageName = ident
enumName = ident
fieldName = ident
oneofName = ident
mapName = ident
serviceName = ident
rpcName = ident
streamName = ident
messageType = [ "." ] { ident "." } messageName
enumType = [ "." ] { ident "." } enumName
groupName = capitalLetter { letter | decimalDigit | "_" }
整數字面量
intLit = decimalLit | octalLit | hexLit
decimalLit = [-] ( "1" ... "9" ) { decimalDigit }
octalLit = [-] "0" { octalDigit }
hexLit = [-] "0" ( "x" | "X" ) hexDigit { hexDigit }
浮點數字面量
floatLit = [-] ( decimals "." [ decimals ] [ exponent ] | decimals exponent | "."decimals [ exponent ] ) | "inf" | "nan"
decimals = [-] decimalDigit { decimalDigit }
exponent = ( "e" | "E" ) [ "+" | "-" ] decimals
布林值
boolLit = "true" | "false"
字串字面量
strLit = strLitSingle { strLitSingle }
strLitSingle = ( "'" { charValue } "'" ) | ( '"' { charValue } '"' )
charValue = hexEscape | octEscape | charEscape | unicodeEscape | unicodeLongEscape | /[^\0\n\\]/
hexEscape = '\' ( "x" | "X" ) hexDigit [ hexDigit ]
octEscape = '\' octalDigit [ octalDigit [ octalDigit ] ]
charEscape = '\' ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | '\' | "'" | '"' )
unicodeEscape = '\' "u" hexDigit hexDigit hexDigit hexDigit
unicodeLongEscape = '\' "U" ( "000" hexDigit hexDigit hexDigit hexDigit hexDigit |
"0010" hexDigit hexDigit hexDigit hexDigit
空語句
emptyStatement = ";"
常量
constant = fullIdent | ( [ "-" | "+" ] intLit ) | ( [ "-" | "+" ] floatLit ) |
strLit | boolLit | MessageValue
MessageValue 在文字格式語言規範中定義。
版本
版本宣告取代了舊的 syntax 關鍵字,用於定義此檔案所使用的版本。
edition = "edition" "=" [ ( "'" decimalLit "'" ) | ( '"' decimalLit '"' ) ] ";"
匯入語句
import 語句用於匯入另一個 .proto 檔案的定義。
import = "import" [ "weak" | "public" ] strLit ";"
示例
import public "other.proto";
包
package 說明符可用於防止協議訊息型別之間的名稱衝突。
package = "package" fullIdent ";"
示例
package foo.bar;
選項
選項可在 proto 檔案、訊息、列舉和服務中使用。選項可以是 protobuf 定義的選項,也可以是自定義選項。有關更多資訊,請參閱語言指南中的選項。選項也可用於控制功能設定。
option = "option" optionName "=" constant ";"
optionName = ( ident | "(" ["."] fullIdent ")" )
示例
option java_package = "com.example.foo";
option features.enum_type = CLOSED;
欄位
欄位是協議緩衝區訊息的基本元素。欄位可以是普通欄位、組欄位、oneof 欄位或對映欄位。欄位具有標籤、型別和欄位編號。
label = [ "repeated" ]
type = "double" | "float" | "int32" | "int64" | "uint32" | "uint64"
| "sint32" | "sint64" | "fixed32" | "fixed64" | "sfixed32" | "sfixed64"
| "bool" | "string" | "bytes" | messageType | enumType
fieldNumber = intLit;
普通欄位
每個欄位都有一個標籤、型別、名稱和欄位編號。它可能還包含欄位選項。
field = [label] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
fieldOptions = fieldOption { "," fieldOption }
fieldOption = optionName "=" constant
示例
foo.bar nested_message = 2;
repeated int32 samples = 4 [packed=true];
Oneof 和 oneof 欄位
oneof 由 oneof 欄位和 oneof 名稱組成。Oneof 欄位沒有標籤。
oneof = "oneof" oneofName "{" { option | oneofField } "}"
oneofField = type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
示例
oneof foo {
string name = 4;
SubMessage sub_message = 9;
}
Map 欄位
map 欄位具有鍵型別、值型別、名稱和欄位編號。鍵型別可以是任何整數或字串型別。請注意,鍵型別不能是列舉。
mapField = "map" "<" keyType "," type ">" mapName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
keyType = "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" |
"fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string"
示例
map<string, Project> projects = 3;
擴充套件和保留
擴充套件和保留是宣告欄位編號範圍或欄位名稱範圍的訊息元素。
擴充套件
擴充套件宣告訊息中的一個欄位編號範圍可用於第三方擴充套件。其他人可以在他們自己的 .proto 檔案中為您的訊息型別宣告帶有這些數字標籤的新欄位,而無需編輯原始檔案。
extensions = "extensions" ranges ";"
ranges = range { "," range }
range = intLit [ "to" ( intLit | "max" ) ]
示例
extensions 100 to 199;
extensions 4, 20 to max;
保留
Reserved 聲明瞭訊息或列舉中不能使用的欄位編號或名稱範圍。
reserved = "reserved" ( ranges | reservedIdent ) ";"
fieldNames = fieldName { "," fieldName }
示例
reserved 2, 15, 9 to 11;
reserved foo, bar;
頂層定義
列舉定義
列舉定義由一個名稱和一個列舉主體組成。列舉主體可以有選項、列舉欄位和保留語句。
enum = "enum" enumName enumBody
enumBody = "{" { option | enumField | emptyStatement | reserved } "}"
enumField = fieldName "=" [ "-" ] intLit [ "[" enumValueOption { "," enumValueOption } "]" ]";"
enumValueOption = optionName "=" constant
示例
enum EnumAllowingAlias {
option allow_alias = true;
EAA_UNSPECIFIED = 0;
EAA_STARTED = 1;
EAA_RUNNING = 2 [(custom_option) = "hello world"];
}
訊息定義
訊息由訊息名稱和訊息體組成。訊息體可以包含欄位、巢狀列舉定義、巢狀訊息定義、擴充套件語句、擴充套件、組、選項、oneof、map 欄位和保留語句。訊息不能在同一個訊息模式中包含兩個同名欄位。
message = "message" messageName messageBody
messageBody = "{" { field | enum | message | extend | extensions | group |
option | oneof | mapField | reserved | emptyStatement } "}"
示例
message Outer {
option (my_option).a = true;
message Inner { // Level 2
required int64 ival = 1;
}
map<int32, string> my_map = 2;
extensions 20 to 30;
}
訊息內部宣告的任何實體都不能有衝突的名稱。以下所有情況都是禁止的
message MyMessage {
string foo = 1;
message foo {}
}
message MyMessage {
string foo = 1;
oneof foo {
string bar = 2;
}
}
message MyMessage {
string foo = 1;
extend Extendable {
string foo = 2;
}
}
message MyMessage {
string foo = 1;
enum E {
foo = 0;
}
}
擴充套件
如果同一 .proto 檔案或匯入的 .proto 檔案中的訊息為擴充套件保留了一個範圍,則該訊息可以被擴充套件。
extend = "extend" messageType "{" {field | group} "}"
示例
extend Foo {
int32 bar = 126;
}
服務定義
service = "service" serviceName "{" { option | rpc | emptyStatement } "}"
rpc = "rpc" rpcName "(" [ "stream" ] messageType ")" "returns" "(" [ "stream" ]
messageType ")" (( "{" { option | emptyStatement } "}" ) | ";" )
示例
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
Proto 檔案
proto = [syntax] { import | package | option | topLevelDef | emptyStatement }
topLevelDef = message | enum | extend | service
一個 .proto 檔案示例
edition = "2023";
import public "other.proto";
option java_package = "com.example.foo";
enum EnumAllowingAlias {
option allow_alias = true;
EAA_UNSPECIFIED = 0;
EAA_STARTED = 1;
EAA_RUNNING = 1;
EAA_FINISHED = 2 [(custom_option) = "hello world"];
}
message Outer {
option (my_option).a = true;
message Inner { // Level 2
int64 ival = 1 [features.field_presence = LEGACY_REQUIRED];
}
repeated Inner inner_message = 2;
EnumAllowingAlias enum_field = 3;
map<int32, string> my_map = 4;
extensions 20 to 30;
reserved reserved_field;
}
message Foo {
message GroupMessage {
bool a = 1;
}
GroupMessage groupmessage = [features.message_encoding = DELIMITED];
}