前言
在上一篇文章初识Protobuf及其编译工具的安装中,我们简单家介绍了如何使用protoc
工具来编译.proto
文件,接下来我们来介绍一下Protobuf
的语法,我们在日常开发工作中如何编写正确的.proto
文件来满足我们的开发需求;
语法介绍
我们以java
语法来类比,创建一个java
类语法如下:
class Name {
}
Protobuf
和上面的语法类似,只不过使用message
替代了class
声明:
message Name {
}
-
基本语法
接下来我们定义一个简单的类型:
syntax = "proto3"; message Person { string name = 1; int32 age = 2; float height = 3; double weight = 4; bool gender = 5; }
从上述代码
Person
中,我们可以大概了解到Protobuf
语法:- 需要声明
syntax
表示Protobuf
语法版本,不声明默认是proto2
;这个声明必须是文件的第一个非空非注释行; - 通过关键字
message
声明一个类型; - 属性声明结构为
[字段类型] [字段名] = [字段编号]
格式;字段编号从1开始,最大的字段数是 536,870,911;[19000~19999]范围编号不可以使用,是预留给Protocol Buffers协议实现;
- 需要声明
-
列表
如果想在消息类型中声明一个列表字段,在
java
中使用List<>
表示,那么Protobuf
中如何编写呢?syntax = "proto3"; message Result{ repeated string snippets = 1; }
我们可以通过关键字
repeated
来表示List
列表或者数组,被repeated
修饰的字段值是可以出现多次的; -
枚举
在定义消息类型时,我们希望其中一个字段只能是预定义的值列表中的一个值。处理这种需求的最好方式当然是定义一个枚举类型啦:
syntax = "proto3"; enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; Corpus corpus = 4; }
枚举类型使用关键字
enum
修饰,枚举数据需要一个常量类映射,第一个枚举映射必须是0;定义枚举类型需要遵守以下规则:- 必须有一个零值,这样我们就可以使用0作为数值默认值。
- 零值必须是第一个元素,以便与 proto2语义兼容,其中第一个枚举值总是默认值。
-
Map键值对
如果你想创建一个关联映射作为你数据定义的一部分,protocol buffers提供了一个方便的快捷语法:
map<key_type, value_type> map_field = N;
key_type
可以是任何整型或字符串类型,枚举不可以作为有效的key_type
。 -
oneof类型
我们在日常开发中可能会在一个消息中有多个字段,并且最多只能同时设置其中一个字段,那么我们就可以通过
oneof
来实现:syntax = "proto3"; message SearchResponse { string code = 1; oneof data { string empty_result = 2; string result = 3; } }
在
SearchResponse
消息类型中,分别有三个属性,但是empty_result
、result
两个属性只能二者取其一,如果同时对empty_result
、result
两个属性赋值,那么最后被赋值的属性才会最终生效;注意:oneof 不支持
repeated
和map
-
import导入定义
如果我们需要导入其他文件中的消息类型,那么我们可以使用
import
关键字来处理:import "myproject/other.proto";
如果我们需要将
.proto
文件移动到新的位置,为了避免修改所有引用了该文件的.proto
文件,我们可以使用import public
解决该问题:移动到新位置
.proto
为new.proto
:// new.proto // All definitions are moved here
我们只需要在原先的
.proto
文件添加一行:// old.proto // This is the proto that all clients are importing. import public "new.proto"; import "other.proto";
这样的话,所有之前引用该
old.proto
文件的还是依旧不变:// client.proto import "old.proto"; // You use definitions from old.proto and new.proto, but not other.proto
-
默认值
在解析消息时,如果消息类型中的属性没有被赋值,那么该属性值将被赋予默认值:
- 对于字符串,默认值为空字符串。
- 对于字节,默认值为空字节。
- 对于布尔值,默认值为 false。
- 对于数值类型,默认值为零。
- 对于枚举,默认值是第一个定义的枚举值,该值必须为0。
-
预留值
在项目迭代的过程中,我们会不断地优化定义的消息类型,如果我们在迭代过程中,有一些字段被删掉了,建议我们将这些被删掉的字段或编号用
reserved
修饰:enum Foo { reserved 2, 15, 9 to 11, 40 to max; reserved "FOO", "BAR"; }
注意,不能在同一个保留语句中混合字段名和数值。
本文正在参加「金石计划」