简介
上一篇文章咱们对 google 的 protobuf 曾经有了一个根本的意识,并且可能应用相应的工具生成对应的代码了。然而对于.proto 文件的格局和具体反对的类型还不是很分明。明天本文将会带大家一探到底。
留神,本文介绍的协定是 proto3 版本的。
定义一个音讯
protobuf 中的主体被称为是 message,能够将其看做是咱们在程序中定义的类。咱们能够在.proto 文件中定义这个 message 对象,并且为其增加属性,如下所示:
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
上例的第一行指定了.proto 文件的协定类型,这里应用的是 proto3,也是最新版的协定,如果不指定,默认状况下是 proto2。
类型定义
这里咱们为 SearchRequest 对象,定义了三个属性,其类型别离是 String 和 int32。
String 和 int32 都是简略类型,protobuf 反对的简略类型如下:
protobuf 类型 | 阐明 | 对应的 java 类型 |
---|---|---|
double | 双精度浮点类型 | double |
float | 浮点类型 | float |
int32 | 整型数字, 最好不示意正数 | int |
int64 | 整型数字,最好不示意正数 | long |
uint32 | 无符号整数 | int |
uint64 | 无符号整数 | long |
sint32 | 带符号整数 | int |
sint64 | 带符号整数 | long |
fixed32 | 四个字节的整数 | int |
fixed64 | 8 个字节的整数 | long |
sfixed32 | 4 个字节的带符号整数 | int |
sfixed64 | 8 个字节的带符号整数 | long |
bool | 布尔类型 | boolean |
string | 字符串 | String |
bytes | 字节 | ByteString |
当然 protobuf 还反对简单的组合类型和枚举类型。
枚举类型在 protobuf 中用 enum 来示意,咱们来看一个枚举类型的定义:
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 开始的,0 也是枚举类型的默认值。
在枚举中,还能够定义具备雷同 value 的枚举类型,然而这样须要加上 allow_alias=true 的选项,如下所示:
message MyMessage1 {
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
}
message MyMessage2 {
enum EnumNotAllowingAlias {
UNKNOWN = 0;
STARTED = 1;
// RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.
}
}
在枚举类型中,如果咱们后续对某些枚举类型进行了删除,那么被删除的值可能会被后续的用户应用,这样就会造成潜在的代码隐患,为了解决这个问题,枚举提供了一个 reserved 的关键词,被这个关键词申明的枚举类型,就不会被后续应用,如下所示:
enum Foo {
reserved 2, 15, 9 to 11, 40 to max;
reserved "FOO", "BAR";
}
reserved 关键字也能够用在 message 的字段中,示意后续不要应用到这些字段,如下:
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
字段的值
咱们能够看到,每个 message 的字段都调配了一个值,每个字段的值在 message 中都是惟一的,这些值是用来定位在二进制音讯格局中的字段地位。所以一旦定义之后,不要随便批改。
要留神的是值 1 -15 在二进制中应用的 1 个字节来示意的,值 16-2047 须要应用 2 个字节来示意,所以通常将 1 -15 应用在最常见的字段和可能反复的字段,这样能够节约编码后的空间。
最小的值是 1,最大的值是 2 的 29 次方 -1,或者 536,870,911。这两头从 19000-19999 是保留数字,不能应用。
当音讯被编译之后,各个字段将会被转成为对应的类型,并且各个字段类型将会被赋予不同的初始值。
strings 的默认值是空字符串,bytes 的默认值是空 bytes,bools 的默认值是 false,数字类型的默认值是 0,枚举类型的默认值是枚举的第一个元素。
字段描述符
每个音讯的字段都能够有两种描述符,第一种叫做 singular,示意 message 中能够有 0 个或者 1 个这个字段,这是 proto3 中默认的定义形式。
第二种叫做 repeated,示意这个字段在 message 中是能够反复的,也就是说它代表的是一个汇合。
增加正文
在 proto 中的正文和 C ++ 的格调相似,能够应用:// 或者 / … / 的格调来正文,如下所示:
/* 这是一个正文. */
message SearchRequest {
string query = 1;
int32 page_number = 2; // 页面的 number
int32 result_per_page = 3; // 每页的后果
}
嵌套类型
在一个 message 中还能够嵌入一个 message,如下所示:
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
在上例中,咱们在 SearchResponse 定义了一个 Result 类型,在 java 中,实际上能够将其看做是嵌套类。
如果心愿在 message 的定义类之外应用这个外部的 message,则能够通过 _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;
}
}
}
Map
如果想要在 proto 中定义 map,能够这样写:
map<key_type, value_type> map_field = N;
这里的 value_type 能够是除 map 之外的任意类型。留神 map 不能是 repeated。
map 中的数据的程序是不定的,咱们不能依赖存入的 map 程序来判断其取出的程序。
总结
以上就是 proto3 中定义申明文件该留神的事项了,大家在应用 protobuf 的时候要多加留神。
本文已收录于 http://www.flydean.com/02-protocolbuf-detail/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!