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 '"' ) ] ";"
导入语句
导入语句用于导入另一个 .proto 的定义。
import = "import" [ "weak" | "public" ] strLit ";"
示例
import public "other.proto";
包
包说明符可用于防止协议消息类型之间的名称冲突。
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;
}
映射字段
映射字段具有键类型、值类型、名称和字段编号。键类型可以是任何整数或字符串类型。请注意,键类型不能是枚举。
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" ( 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、映射字段和保留语句。一条消息不能在同一个消息架构中包含两个名称相同的字段。
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 文件中的消息保留了扩展的范围,则可以扩展该消息。
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];
}