Protocol Buffers(Protobuf) 官方文档--Protobuf语言指南html
约定:为方便书写,ProtocolBuffers在下文中将已Protobuf代替。java
本指南将向您描述如何使用protobuf定义i结构化Protobuf数据,包括.proto文件语法和如何使用.proto文件生成数据存取类。python
做为一个参考指南,本文档将以示例的形式一步步向您介绍Protobuf的特色。您能够参考您所选择的语言的示例。tutorial 编程
--------------------------------------小小的分割线-----------------------------------------数组
定义一个消息类型ide
首先,看一个很是简单的例子,好比说你想定义一个 搜索请求消息 ,每一个搜索请求都有一个 查询的字符串(关键字:好比咱们上百度搜索 《报告老板》),和咱们搜索出来的一个感兴趣的网页,以及搜索到的全部网页总数。 来看看这个.proto文件是如何定义的。ui
1 message SearchRequest { 2 required string query = 1; 3 optional int32 page_number = 2; 4 optional int32 result_per_page = 3; 5 }
这个"搜索请求"消息指定了三个字段(名称/属性 组合),每个你想要包含在这类型的信息内的东西,都必须有一个字段,每一个字段有一个名称和类型!google
在上面的示例中,全部的字段都是标量类型(scalar types):两个整数(integers:page_number
和 result_per_page)和一个字符串(string:query:查询的关键字),不过你能够在你的字段内指定符合类型。包括枚举类型(enumerations)
和其余的消息类型编码
如你所见,每一个消息的字段都有一个惟一的数字标签,这些标签用来表示你的字段在二进制消息(message binary format)中处的位置。而且一旦指定标签号,在使用过程当中是不能够更改的,标记这些标签号在1-15的范围内每一个字段须要使用1个字节用来编码这一个字节包括字段所在的位置和字段的类型!(须要更多关于编码的信息请点击Protocol Buffer Encoding)。标签号在16-2047须要使用2个字节来编码。因此你最好将1-15的标签号为频繁使用到的字段所保留。若是未来可能会添加一些频繁使用到的元素,记得留下一些1-15标签号。spa
最小可指定的标签号为1,最大的标签号为229 - 1或者536870911。不能使用19000-19999的标签号(FieldDescriptor::kFirstReservedNumber 至 FieldDescriptor::kLastReservedNumber) 这些标签号是为protobuf内部实现所保留的,若是你在.proto文件内使用了这些标签号Protobuf编译器将会报错!
消息字段能够被指定为如下三种:
required
: 完整的消息内必须拥有此字段。此字段是必须拥有的 (双方都要有)optional
: 完整的消息内此字段是可选的,可拥有也能够没有(双方可选)repeated
: 完整的消息内本字段的值能够拥有任意个,重复的值的次数会保存下来。(双方可选,数组)由于历史的缘由:repeated字段若是是基本的数字类型的话会没法编码。新的代码应该使用特殊的关键字[packed=true] 来使其获得有效的编码.例如
注意:你应该当心将字段设置为required,若是你但愿在某些状况下取消required字段的读写,它将改变字段为optional属性,旧的的读取方将会认为此消息不彻底。可能会无心的将其丢弃。你应该考虑自定义一个消息检查程序。google的一些工程师认为使用optinal字段的好处大于required。可是显然这个观点并非通用的。
多个消息类型能够定义在同一个.proto文件内,这对定义多个有关联的消息是是十分有用的。例如,若是你想定义一个用于回复SearchResponse消息,你能够像这样在.proto内添加。
添加注释
添加注释的方式和C/C++是同样的。使用//
当你使用protobuf编译器编译一个.proto文件,它会生成在.proto内你描述的消息类型的操做代码,这些代码是根据你所选择的编程功能语言决定的。这些操做代码内包含了设置字段值 和读取字段值,以及序列化到输出流 和 从输入流反序列化。
C++:编译器会按照每一个.proto文件生成与其对应的.h和.cc文件,每一个消息相似都有独立的消息操做类。
Java:编译器将会生成一个.java文件和一个操做类,此操做类为全部消息类型所共有, 使用一个特别的Builder类为每一个消息类型实例化
.
Python:有一点不一样 – 编译器会为每一个消息生成一个模块每一个模块有一个静态描述符, 该模块与一个元类在运行时建立一个须要的数据操做类。
从你所选择语言的例程,你能够找到更多关于API的内容, 须要关于API的详细信息, 参考: API reference.
一个消息的字段若是要使用标量可以使之为如下类型 –这个表格显示了在.proto文件内能够指定的类型, 与自动生成的相对类型!
.proto Type | Notes | C++ Type | Java Type | Python Type[2] |
---|---|---|---|---|
double | double | double | float | |
float | float | float | float | |
int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int |
int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long[3] |
uint32 | Uses variable-length encoding. | uint32 | int[1] | int/long[3] |
uint64 | Uses variable-length encoding. | uint64 | long[1] | int/long[3] |
sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int |
sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long[3] |
fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 228. | uint32 | int[1] | int |
fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 256. | uint64 | long[1] | int/long[3] |
sfixed32 | Always four bytes. | int32 | int | int |
sfixed64 | Always eight bytes. | int64 | long | int/long[3] |
bool | bool | boolean | boolean | |
string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode[4] |
bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str |
你能够在Protocol Buffer Encoding.找到更多关于.这些类型如何编码,如何序列化定义消息的信息!
[1] 在Java中, 无符号32位和64位整数与其有符号相对应, 最高位用来保存符号!
[2] 在全部状况下, 设置某个字段的值将会执行类型检查确保其值是合法的!
[3] 64位或32位无符号整数在解码中会以long来解码, 给字段赋值的时候能够是int.可是在全部状况下,赋值的时候会转变为其目标类型 . 详见 [2].
[4] Python的字符串在解码时候会以unicode来描述,可是一样的能够给其赋值为ascii字符串 (此乃弦外之音).
如上所述,在描述一个消息的时候能够用optional指定字段约束,一个消息能够包含也能够不包含optional元素。当一个消息被解析,若是其没有一个optional字段,被解析的消息对象就会将其相对的字段设置为其字段的默认值。这个默认值能够在描述消息的时候被指定。例如。好比你想设置SearchRequest的
result_per_page的默认值为10.
若是一个optional字段没有被指定其默认值。其默认值被自动替换为:
1.字符串:为空字符串.
2.bool:为false.
3.数字类型:为0;
4.枚举值:为第一个枚举值
当你定义消息格式的时候, 也许你但愿其中的一个字段的的值为一个预约义的值类表中的一个. 比方说, 在SearchRequest消息中
你想定义一个 corpus
字段, corpus字段的值能够为:" UNIVERSAL
, WEB
, IMAGES
, LOCAL
, NEWS
, PRODUCTS
或者 VIDEO"
. 你能够很是简单的给你的消息添加一个枚举类型 - 一个枚举字段类型其值指定被指定为一个常量的集合 (若是你尝试赋值一个不同的值, 解析器将会认为这个字段为未知字段). 在下面的例子中 咱们给corpus字段指定为枚举类型与其可能的值 :
你能够为一个枚举常量定义别名,若是你须要这样作的话须要将allow_alias设置为true。不然若是出现别名的话编译器将会报错!
枚举值的范围必须在32位整数以内.枚举值的编码使用可变长度的整数,负数会很是低效因此,不推荐使用。你能够在一个消息内部定义一个枚举类型,好比上面的例子。或者也能够在消息的外部定义。这些枚举类型是能够在.proto文件内中重用的,你能够在消息内定义个枚举类型。而后在不一样的消息类型中使用它!可使用 MessageType.EnumType来访问。
当你运行编译器编译.proto文件中的枚举类型时,生成的代码会有一个相对应的枚举值(JAVA 或者C++),或者有一个特别的EnumDescriptor类(python)用于在运行时生成一个符号常量集合。
更多关于枚举类型的信息查询 generated code guide 选择你使用的语言。
待续....................