标签:针对 告警 wan wrapper max name 替换 结束 ret
这篇指南讲述如何使用Protocol Buffers来结构化你的Protocol Buffer数据,包括.proto
文件语法以及如何从.proto
文件生成你的访问类型。本文主要涵盖了proto3的语法,proto2的语法参见Proto2 Language Guide。
这是一篇参考教程 -- 本文中诸多功能的分步示例,详见tutorial。
首先来看一个非常简单的例子。假设你想定义一个搜素请求的消息格式,其中每个搜索请求都包含一个检索字段、特定的结果页(你感兴趣的结果所在的页面)以及每个页面的结果数量。你可以使用下面的.proto
文件来定义消息类型。
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
proto3
语法:如果你不指定的话,protocol buffer编译器将默认你使用的是proto2
。这必须写在文件中非空、非注释的第一行。SearchRequest
消息明确定义了三个字段(键值对),对应每一条你想包含在这个消息类型中的数据。每个字段都有一个名称和类型。在上面的例子中,所有的字段都是明确类型的:两个integers(page_number
和result_per_page
)和一个string(query
)。当然,你也可以将你的字段指定成复合类型,包括枚举和其他消息类型。
如你所见,在定义的消息中的每个字段都有一个唯一的序号。这些序号用来在二进制消息结果中标识你的字段,而且一旦使用了消息类型,就不应该再变动。注意,字段序号在1到15的范围内占用1个字节来编码,包括字段序号和字段类型(详见Protocol Buffer Encoding)。字段序号在16到2047范围内占两个字节。所以你应该为经常使用的消息元素保留1到15的序号。切记为将来可能新增的常用元素预留一些空间。
你能使用的最小字段序号为1,最大为\(2^{29}-1\),或 536,870,911。但是你不能使用19000到19999(FieldDescriptor::kFirstReservedNumber
到FieldDescriptor::kLastReservedNumber
),因为它们是为Protocol Buffers实现预留的,如果在你的.proto
文件中使用了,protocol buffer编译器会报错。同样,你也不能使用任何以前保留的字段序号。
消息字段可以遵循下列规则之一:
在proto3中,repeated
字段的标量数字默认使用packed
编码。关于packed
编码,详见Protocol Buffer Encoding。
在单个.proto
文件中可以定义多个消息类型。这在你定义多个关联的消息类型时非常有用,例如,如果你想定义应答消息格式来满足你的SearchResponse
消息类型,你可以在同一个.proto
文件中添加:
...
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...
}
要在你的.proto
文件中添加注释,可以使用C/C++风格的//
和/* ... */
语法。
/* SearchRequest represents a search query, with pagination options to
* indicate which results to include in the response. */
message SearchRequest {
string query = 1;
int32 page_number = 2; // Which page number do we want?
int32 result_per_page = 3; // Number of results to return per page.
}
如果你通过完全删除或注释一个字段来更新消息类型,那么此后的用户在更新他们自己的类型时将可以重用该字段的序号。如果之后他们使用旧版的.proto
时,会引起严重的问题,包括数据损坏、隐私bug等。避免给问题的途径之一就是指明你要删除的字段需要(或者会在JSON序列化时会引起问题的名称)是reserved
的,这样将来用户在使用这些字段时protocol buffer编译器就会告警。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
注意,不能在同一个reserved
语句中混用字段名称和字段序号。
.proto
文件会生成什么?当你使用protocol buffer 编译器时,编译器会根据你选定的语言来生成你.proto
文件中描述的消息类型,包括获取和设置字段的值,序列化你的消息到一个输出流中,从输入流中解析你的消息。
.proto
文件生成一个.h
和一个.cc
,你定义的每个消息类型都会变成一个类。.java
文件,包含每个消息类型的类,同时还会指明一个Builder
类来创建消息类的实例。.proto
文件每一消息类型的静态描述,之后在运行时通过基类来创建必要的Python数据访问类。.pb.go
的文件,包含文件中每个消息类型。.rb
的文件,包含消息类型的Ruby模块。.proto
文件生成一个pbobjc.h
和一个pbobjc.m
文件,你定义的每个消息类型都会变成一个类。.proto
文件生成一个.cs
文件,你定义的每个消息类型都会变成一个类。.pb.dart
文件,文件中定义的每个消息类型都会变成一个类。你可以在之后的教程中找关于对应语言的APIs的使用。更多APIs细节,详见API reference。
标量字段可以是下面类型中的任意一个。下表展示了.proto
文件中标明的类型,以及在自动生成的类中对应的类型:
.proto 类型 | 说明 | C++类型 | Java类型 | Python类型\(^{[2]}\) | Go类型 | Ruby类型 | C#类型 | PHP类型 | Dart类型 |
---|---|---|---|---|---|---|---|---|---|
double | double | double | float | float64 | Float | double | float | double | |
float | float | float | float | float32 | Folat | float | float | double | |
int32 | 使用可变长度编码。编码负数低效,如果字段可能有负数,使用sint32代替。 | int32 | int | int | int32 | Fixnum or Bignum(as required) | int | integer | int |
int64 | 使用可变长度编码。编码负数低效,如果字段可能有负数,使用sint64代替。 | int64 | long | int/long\(^{[3]}\) | int64 | Bignum | long | integer/string\(^{[5]}\) | int64 |
uint32 | 使用可变长度编码。 | uint32 | int\(^{[1]}\) | int/long\(^{[3]}\) | uint32 | Fixnum or Bignum(as required) | uint | integer | int |
uint64 | 使用可变长度编码。 | uint64 | long\(^{[1]}\) | int/long\(^{[3]}\) | uint64 | Bignum | ulong | integer/string\(^{[5]}\)}$ | int64 |
sint32 | 使用可变长度编码。有符号整数。编码负数比普通int32高效。 | int32 | int | int | int32 | Fixnum or Bignum(as required) | int | integer | int |
sint64 | 使用可变长度编码。有符号整数。编码负数比普通int64高效。 | int64 | long | int/long\(^{[3]}\) | int64 | Bignum | long | integer/string\(^{[5]}\) | int64 |
fixed32 | 4字节。如果变量大于2\(^{28}\)比uint32高效。 | uint32 | int\(^{[1]}\) | int | int32 | Fixnum or Bignum(as required) | int | integer | int |
fixed64 | 8字节。如果变量大于2\(^{56}\)比uint64高效。 | uint64 | long\(^{[1]}\) | int/long\(^{[3]}\) | uint64 | Bignum | ulong | integer/string\(^{[5]}\) | int64 |
sfixed32 | 4字节 | uint32 | int\(^{[1]}\) | int | int32 | Fixnum or Bignum(as required) | int | integer | int |
sfixed64 | 8字节 | uint64 | long\(^{[1]}\) | int/long\(^{[3]}\) | uint64 | Bignum | ulong | integer/string\(^{[5]}\) | int64 |
bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | bool | |
string | 必须是UTF-8编码或7-bit的ASCII文本,长度不能大于2\(^{32}\) | string | String | str/unicode\(^{[4]}\) | string | String(UTF-8) | string | string | String |
bytes | 可以包含任何长度不超过232的字节序列 | string | ByteString | str | []byte | String(ASCII-8BIT) | ByteString | string | List<int> |
更多编码细节,详见Protocol Buffer Encoding。
\(^{[1]}\)在Java中,无符号32位和64位整数使用它们的有符号对应的整数表示,顶部的整数只存储在符号位中。
\(^{[2]}\)在所有场景中,给字段设置值时将调用类型检查来确保有效。
$^{[3]}$64位或无符号32位整数会解码成对应的长度,但如果在设置字段时给定的是int,也可以解码为int。
\(^{[4]}\)Python strings会解码成unicode,但如果给定的是ASCII string,会被解码成str。
\(^{[5]}\)Integer用在64位机器上,string用于32位机器。
在解析消息时,如果编码的消息不包含特定的singular元素,则解析对象中的相应字段将设置为该字段的默认值。这些默认值与类型有关:
对于repeated字段,其默认值为空(通常是目标语言的空列表)。
对于标量消息字段来说,一旦消息被解析,就无法判断该字段是真实被设为默认值(例如bool变量被设为false)还是就没有设置:在定义消息类型时需要牢记这一点。例如,如果你不想在默认情况向执行某种行为,那么就不要用boolean被设置为false
来切换这些行为。同时,如果一个标量消息字段被设为它的默认值,那么改值在传输时将不会被序列化。
在生成代码时,在你所选的语言中默认值如何工作,详见generated code guide。
当你在定义消息类型时,你可能想让它的字段只是用预定义列表中的一个值。例如,假如你想给SearchRequest
添加一个corpus
字段,其值可以是UNIVERSAL
、WEB
、IMAGE
、LOCAL
、NEWS
、PRODUCT
或者VIDEO
。通过enum
,在你的消息中定义一个包含每个可能值得枚举变量,可以很简单地做的。
在下面的例子中,我们添加了一个名为Corpus
的enum
类型,它包含了所有可能的值,和一个Corpus
类型的字段:
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的常量作为第一个元素。因为:
你可以定义别买来给不同的枚举常量分配相同的值。这样就需要你将allow_alias
设置为true
,否则编译器在发现别名时会产生错误消息。
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
enum EnumNotAllowingAlias {
UNKNOWN = 0;
STARTED = 1;
// RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.
}
枚举常量取值范围必须在32位正整数之间。因为enum
变量在传输时使用varint encoding,负数是低效且不推荐的。你可以如上述例子一样在消息定义内部定义enum
,也可以在外部定义,这样的enum
可以被.proto
文件中的其它消息定义使用。你也可以使用MessageType.EnumType
语法将一个消息的enum
声明类型作为另一个不同消息的字段类型。
当你编译一个使用了enum
的.proto
文件时,生成的代码中会包含Java或C++对应的enum
,针对Python的特定的EnumDescriptor
类,用来在执行生成的类中创建一系列包含数值的符号常量。
在反序列化期间,无法识别的enum值将保留在消息中,尽管在反序列化消息时如何表示该值取决于语言。在支持指定符号范围之外使用值的开放枚举类型的语言,如c++和Go,未知的枚举值只是作为其基础整数表示形式存储。在具有封闭枚举类型的语言,如Java,枚举中的大小写用于表示无法识别的值,并且可以使用特殊的访问器访问底层整数。在任何一种情况下,如果消息被序列化,未被识别的值仍将与消息一起序列化。
在你所选的语言中,带有enum
的消息如何工作,详见generated code guide。
如果你通过完全删除或注释一个字段来更新枚举类型时,那么之后的用户在更新他们自己的类型时将可以重用该字段的序号。如果之后他们使用旧版的.proto
时,会引起严重的问题,包括数据损坏、隐私bug等。避免给问题的途径之一就是指明你要删除的字段需要(或者会在JSON序列化时会引起问题的名称)是reserved
的,这样将来用户在使用这些字段时protocol buffer编译器就会告警。你可以指明你要保留的数字到可能的最大值(通过max
关键字)得范围。
enum Foo {
reserved 2, 15, 9 to 11, 40 to max;
reserved "FOO", "BAR";
}
注意,不能在同一个reserved
语句中混用字段名称和字段序号。
你也可以使用其它消息类型作为字段类型。例如,假如你想在SearchResponse
消息中包含一个Result
消息,你可以在同一个.proto
文件中定义一个Result
消息类型,然后在SearchResponse
中声明一个Result
类型的字段。
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
在上面的例子中,Result
消息类型和SearchResponse
定义在同一个.proto
文件中,如果你要用来的字段类型已经在其它的.proto
文件中定义了呢?
你可以通过从其它.proto
文件中导入它们来使用这些定义。要使用其它.proto
的定义,你需要在你的文件头部导入声明:
import "myproject/other_protos.proto";
默认情况下,你只能使用直接导入的.proto
文件。但有时候你可能需要将.proto
文件移到新的路径。相比于直接移到.proto
文件然后更新所有用到它的地方,现在你可以在旧的路径下放置一个虚拟的.proto
文件,以便使用import public概念将所有导入转发到新位置:
// new.proto
// All definitions are moved here
// old.proto
// This is the proto that all clients are importing.
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto
编译器在一系列指定的目录(命令行下通过-I / --proto_path
标志指定)下查找导入的文件。如果没有指定,编译器将在当前目录下查找。通常你应该将--proto_path
标志设为项目的根目录,并且使用全路径导入。
可以在你的proto3消息中导入并使用proto2的消息类型,反之亦可。然而proto2的枚举不能再proto3中直接使用(可以在导入的proto2的消息中使用)。
你可以在一个消息类型中定义并使用其它的消息类型,就像下面的例子 -- Result
消息定义在SearchResponse
中:
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
如果你想在父消息类型外重用该消息,可以使用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;
}
}
}
如果已存在的消息类型不再满足你的需求 -- 例如,你想在消息格式中添加新的字段,但还想使用就格式生成的代码。别担心!在不破坏你现有代码的基础上更新消息类型很简单。只需要记住下面的规则:
.proto
的未来用户就不会意外地重用该号码。int32
、uint32
、int64
、uint64
和bool
都是兼容的——这意味着你可以将一个字段从这些类型中的一种更改为另一种,而不会中断向前或向后兼容。如果从连线中解析出一个不适合相应类型的数字,那么你将获得与在c++中将该数字强制转换为该类型相同的效果(例如,如果将64位数字读取为int32,那么它将被截断为32位)。sint32
和sint64
是相互兼容的,但不与其它整型兼容。string
和bytes
兼容,bytes
与UTF-8
兼容。bytes
兼容。fixed32
与sfixed32
、fixed64
和sfixed64
兼容。enum
与int32
、uint32
、int64
、uint64
兼容(注意变量不兼容的部分将被截断)。然而需要留意的是在消息反序列化时,客户端代码会被区别对待:例如,尽管无法识别的proto3中的enum
类型会被保存在消息中,但是在消息反序列化时,它是如何表示这取决于语言。int字段总会保留它的值。oneof
成员中的单个变量是安全且二进制兼容的。如果您确定没有代码一次设置多个字段,那么将多个字段移动到一个新的字段中可能是安全的。将任何字段移动到现有字段中都是不安全的未知字段是protocol buffer在序列化数据时无法解析的数据。例如,当旧的二进制代码在解析带有新字段的新二进制代码发送的数据时,这些新字段将成为旧二进制代码中的未知字段。
最初,在解析时proto3总是丢弃未知字段,但在3.5版本之后,重新引入了未知字段的保留来匹配proto2的行为。在3.5及之后的版本中,在解析时未知字段会被保留并将其包含的序列化的输出中。
Any
消息类型允许你在没有.proto
定义的情况下将你的消息类型作为嵌入类型使用。Any包含作为bytes
的任意序列化消息,以及充当全局惟一标识符并解析为该消息类型的URL。要使用Any
类型,你需要导入google/protobuf/any.proto
。
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}
给定消息类型的默认URL类型为type.googleapis.com/packagename.messagename
。
不同的语言实现以类型安全的方式来提供打包和解包Any
变量的运行时库帮助程序。例如,在Java中,Any类型使用特定的pack()
和unpack
访问,而在C++中,使用PackFrom()
和UnpackTo()
方法:
// Storing an arbitrary message type in Any.
NetworkErrorDetails details = ...;
ErrorStatus status;
status.add_details()->PackFrom(details);
// Reading an arbitrary message from Any.
ErrorStatus status = ...;
for (const Any& detail : status.details()) {
if (detail.Is<NetworkErrorDetails>()) {
NetworkErrorDetails network_error;
detail.UnpackTo(&network_error);
... processing network_error ...
}
}
目前,用于处理Any类型的运行时库都在开发中。
如果有有一个包含多个字段的消息,在同一时间最多只能设置一个字段,那么你可以通过使用oneof特性强制执行此行为并节省内存。
除所有字段共享同一个Oneof内存和最多同时只能设置一个字段外,Oneof字段与常规字段类似。设置oneof字段中的任何成员都将自动清除其它成员。根据你所使用的的语言不同,你可以使用(必要时)特定的case()
或WhichOneof()
方法来检查Oneof中的哪个变量被设置。
要在你的.proto
文件中定义一个Oneof字段,你可以在的oneof
关键字后跟上你的oneof名称,就如下面的test_oneof
:
message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}
之后你可以添加你的oneof字段到oneof定义中。除了不能使用repeated
字段,你可以使用任意字段。
在你生成的代码中,oneof字段有着与常规字段一样的getters
和setters
。必要时,你也可以使用特定的方法来确定oneof中的哪个值被设置。关于你所选语言的oneof API,详见API reference。
SampleMessage message;
message.set_name("name");
CHECK(message.has_name());
message.mutable_sub_message(); // Will clear name field.
CHECK(!message.has_name());
repeated
。set_name()
方法时sub_message
已经删除。SampleMessage message;
SubMessage* sub_message = message.mutable_sub_message();
message.set_name("name"); // Will delete sub_message
sub_message->set_... // Crashes here
Swap()
来交换两个带有oneofs的消息,每个消息会以另一个的oneof case结束:在下面的例子中,msg1
将拥有sub_message
,msg2
将拥有name
。SampleMessage msg1;
msg1.set_name("name");
SampleMessage msg2;
msg2.mutable_sub_message();
msg1.swap(&msg2);
CHECK(msg1.has_sub_message());
CHECK(msg2.has_name());
在新增或移除oneof字段时要慎重。如果检测到oneof的返回值为None/Not_SET
,可能意味着这个oneof尚未设置或已在不同版本的oneof中设置。无法区分这两者之间的不同,因为无法确定传输中的未知字段是否是给oneof的成员。
如果你想创建一个关联映射作为你的数据定义的一部分,protocol buffers提供了一个方便快捷的语法:
map<key_type, value_type> map_field = N;
这里的key_type
可以是任意的integral或string类型(即除了浮点型和bytes
外的所有标量类型)。注意enum
不是有效的key_type
。value_type
可以是除了其它Map外的所有类型。
那么,假如你想创建一个项目映射,每个项目关联一个string键,定义如下:
map<string, Project> projects = 3;
repeated
。.proto
生成文本格式时,映射根据键排序。数字键按数字大小排序。Proto3现已全面支持生成Map API。关于不同语言的Map API,详见API reference。
在网络中,Map语法等效如下示例,因此即便proto buffers实现不支持的Maps也能处理你的数据:
message MapFieldEntry {
key_type key = 1;
value_type value = 2;
}
repeated MapFieldEntry map_field = N;
所有的protocol buffers实现都必须能产生和接受上述定义所接受的数据。
你可以在.proto
文件中添加package
说明符来避免协议消息类型键的名称冲突。
package foo.bar;
message Open { ... }
之后在定义你的消息类型字段时,你可以使用package
说明符:
message Foo {
...
foo.bar.Open open = 1;
...
}
package
说明符影响生成代码的方式依赖于你所选的语言:
Open
位于foo::bar
命名空间中。package
作为Java包使用,除非在.proto
文件中额外提供option java_package
。package
指令会被忽略,Python模块是根据它们在文件系统中的位置来组织的。package
将被用作Go的包名,除非在.proto
文件中额外提供option go_package
。Open
位于foo::bar
命名空间中。package
在被转换为PascalCase
后作为命名空间使用,除非在.proto
文件中额外提供option csharp_namespace
。Protocol buffer语言中的类型名称解析类似于C++:首先在最内层查找,之后是下一层,一次类推,每个包在其父包的“内部”。“.”开头(例如,.foo.bar.Baz
)意味着从最外层作用域开始查找。
Protocol buffer编译器通过导入的.proto
文件来解析所有的类型名称。即使有着不同的作用域规则,各语言生成的代码也知道如何每种类型该如何使用。
如果你现在RPC(远程调用)系统中使用你的消息类型,你可以在.proto
文件中定义RPC服务接口,之后protocol buffer编译器会生成所选语言的服务接口代码和存根。比如,你要定义一个RPC服务,它使用你的SearchRequest
并返回SearchResponse
,在.proto
文件中你可以这样定义:
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
使用protocol buffer最直接的RPC系统是gRPC:由Google开发的,与语言和平台无关的开源RPC系统。gRPC与protocol buffer协同良好,它允许你使用特殊的protocol buffer插件直接从.proto
文件中生成相关的RPC代码。
如果你不想使用gRPC,你也可以在你自己的RPC实现中使用protocol buffer。详见Proto2 Language Guide。
也有一些正在进行的第三方项目来为protocol buffer开发RPC实现。有关我们所知项目的链接列表,请参阅third-part add-ons wiki page。
Proto3支持Json编码规范,这使得在不同系统间共享数据变得更加方便。在下面的表中,将逐个类型地描述编码。
如果一个值在JSON编码中丢失或为null
,在解析到protocol buffer时它会被解释为合适的默认值。如果protocol buffer中的字段有默认值,那么在Json编码的数据中将默认省略该字段,以节省空间。在Json编码输出中,实现可以提供带有默认字段的选项。
proto3 | Json | Json示例 | 备注 |
---|---|---|---|
message | object | {"fooBar":v,"g":null,_} | 生成Json对象。消息字段名称被映射为lowerCamelCase并成为Json对象的键。如果指定了json_name字段选项,则指定的值将被作为键使用。解析器既接受lowerCamelCase名称(或使用json_name指定的名称),也接受原生的proto字段名称。所有字段类型都可接受null,并被视为该类型的默认值。 |
enum | string | "FOO_BAR" | 使用proto中指定的enum值名称。解析器既接受枚举名称,也接受整数值。 |
map<K,V> | object | {"K":v,_} | 所有的键都被转换成string。 |
repeated V | array | [v, ...] | null被当做空列表[]。 |
bool | true,false | true,false | |
string | string | "Hello World!" | |
bytes | base64 string | "YWJjMTIzIT8kKiYoKSctPUB+" | Json值会变成使用添加padding的标准base64编码的string。标准的或url安全的base64编码,带/不带padding也都可以接受。 |
int32,fixed32,uint32 | number | 1,-10,0 | Json值会变成十进制的数字。数字或string都可被接受。 |
int64,fixed64,uint64 | string | "1","-10" | Json值会变成十进制的string。数字或string都可被接受。 |
float,double | number | 1.1,-10.0,0,"NaN","Infinity" | Json值会变成数字或"NaN"、"Infinity"、"-Infinity"其中之一。数字或string都可被接受。指数表示法也被接受。 |
Any | object | {"@type":"url","f":v,...} | 如果Any包含的值有特定的Json映射,它将被转换为如下格式:{"@type": xxx, "value": yyy}。否则,该值会被转换为Json对象,且”@type“字段会被插入以指示实际数据类型。 |
Timestamp | string | "1972-01-01T10:00:20.021Z" | 使用RFC 3339,其生成的输出总是Z-normalized后的,并使用0、3、6或9位小数。除“Z”以外的偏移量也可以接受。 |
Duration | string | "1.000340012s","1s" | 根据所需的精度,生成的输出总是包含0、3、6或9位小数,跟后缀”s“。只要符合纳秒精度和后缀“s”的要求,任何小数(也可以没有)都可以接受。 |
Struct | object | { ... } | 任意的Json对象。参见struc.proto |
Wrapper types | various types | 2,"2","foo",true,"true",null,0,... | 包装器使用与包装的原始类型相同的JSON表示,但在数据转换和传输期间允许并保留null。 |
FieldMask | string | "f.fooBar,h" | 参见field_mask.proto |
ListValue | array | [foo,bar, ...] | |
Value | value | 任意的Json值 | |
NullValue | null | Json null | |
Empty | object | {} | 任意的空Json对象。 |
Proto3的Json实现可支持下列选项:
proto
文件中的各个声明可以用许多选项进行注释。选项不会改变声明的总体含义,但可能影响在特定上下文中处理它的方式。可用选项的完整列表在google/protobuf/description.proto
中定义。
有些选项是文件级别的,意味着它们应该写在开头位置,而不是在消息、枚举或服务定义中。有些选项是消息级别的,意味着它们应该写在消息定义中。有些选项是字段选项,意味着它们应该写在字段定义中。选项也可以写在枚举类型、枚举值、服务类型和服务方法中,然而,当前不存在对Any有用的选项。
下面是一些常用的选项:
java_package
(文件级):这个包你想用来生成Java类。如果.proto
文件中没有额外给出java_package
选项,默认情况下使用proto包(在.proto
文件中使用package关键字指明的)。然而通常情况下proto包并不适合Java包,因为不希望proto包以反向域名展开。如果不生成Java代码,此项无效。option java_package = "com.example.foo";
java_multiple_files
(文件级):使顶级消息、枚举和服务在包级别定义,而不是在以.proto
文件命名的外部类中定义。option java_multiple_files = true;
java_outer_classname
(文件级):希望生成的最外层Java类的类名(以及文件名)。如果在.proto
文件中没有指定显式的java_outer_classname
,那么将通过将.proto
文件名转换为驼峰写法(比如foo_bar.proto
变为FooBar.java
)来构造类名。如果不生成Java代码,则此选项无效。option java_outer_classname = "Ponycopter";
optimize_for
(文件级):可被设为SPEED
、CODE_SIZE
或LITE_RUNTIME
。这将通过以下方式影响C++和Java代码生成(也可能影响第三方生成):
SPEED
(默认):Protocol buffer编译器会为你的消息类型生成序列化、解析和其它常用操作的代码。此代码高度优化。CODE_SIZE
:Protocol buffer编译器会生成最小的类,其依赖共享、反射的代码来实现序列化、解析和其它操作。因此生成的代码比SPEED
小很多,但操作也会比较慢。Classes仍会实现与SPEED
模式相同的公共API。这种模式在包含大量.proto
文件且不是所有文件都需要快速生成的应用程序中最有用。LITE_RUNTIME
:Protocol buffer编译器依赖“轻量的”运行时库(使用libprotobuf-lite
而不是libprotobuf
)。lite运行时比完整的库小得多(大约小一个数量级),但是忽略了某些特性,比如描述符和反射。这对于在受限平台(如移动电话)上运行的应用程序尤其有用。编译器仍然会像在SPEED
模式下那样生成所有方法的快速实现。生成的类将仅用每种语言实现MessageLite
接口,该接口只提供完整Message
接口方法的一个子集。
option optimize_for = CODE_SIZE;
cc_enable_arenas
(文件级):为C++代码生成启用arena allocation。objc_class_prefix
(文件级):为.proto
文件生成的所有Objective-C类设置前缀。没有默认值。你应该使用苹果推荐的前缀,即3-5个大写字母。注意苹果保留所有的2个字母的前缀。deprecated
(文件级):如果设置为true
,则表示该字段已被废弃,新代码不应使用该字段。在大多数语言中,该选项并没有实际效果。在Java中,会变成一个@Deprecated
注释。将来,其它语言的代码生成器可能会在字段访问器上生成弃用注释,这将使得编译器在尝试使用该字段时发出警告。如果该字段将不再使用且你也不希望有新的用户使用它,那么可以考虑使用保留语句替换字段声明。int32 old_field = 6 [deprecated=true]
Protocol buffer也允许你定义并使用自定义的选项。这是大多数人用不到的高级功能。如果你真的想创建自定义选项,详见Proto2 语言指南。注意,用扩展来创建自定义选项,这是proto3中唯一允许使用的自定义选项。
要从.proto
文件中包括你定义的消息类型的Java、Python、C++、Go、Ruby、Objective-C或C#代码,你需要允许protocol buffer编译器protoc
。如果你还没安装编译器,可从这里下载并根据README编译安装。对于Go,你还需要安装特定的生成插件:在这里你可以找到它。
Protocol编译器使用如下:
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto
IMPORT_PATH
指明解决import
命令时查找.proto
文件的路径。缺省使用当前目录。多个导入命令可以通过多次使用--proto_path
选项指明,它们将按顺序检索。--proto_path
可简写为-I=IMPORT_PATH
。--cpp_out
在DST_DIR
目录中生成C++代码。详见C++ 生成代码引用。--java_out
在DST_DIR
目录中生成Java代码。详见Java 生成代码引用。--python_out
在DST_DIR
目录中生成Python代码。详见Python 生成代码引用。--go_out
在DST_DIR
目录中生成Go代码。详见Go 生成代码引用。--ruby_out
在DST_DIR
目录中生成Ruby代码。详见Ruby 生成代码引用。--objc_out
在DST_DIR
目录中生成Object-C代码。详见Object-C 生成代码引用。--csharp_out
在DST_DIR
目录中生成C#代码。详见C# 生成代码引用。--php_out
在DST_DIR
目录中生成PHP代码。详见PHP 生成代码引用。作为额外的便利,如果DST_DIR
以.zip
或.jar
,编译器将生成指定名称的ZIP格式的压缩包。.jar
输出还将根据Java JAR规范的要求提供一个清单文件。注意如果输出文件已存在,那么它将被重写,编译器并不会生成一个新的副本。
你必须提供一个或者多个.proto
文件作为输入。多个.proto
文件可以一次指定。虽然这些文件是相对于当前目录命名的,但是每个文件必须驻留在IMPORT_PATH
中的一个,以便编译器可以确定它的规范名称。
标签:针对 告警 wan wrapper max name 替换 结束 ret
原文地址:https://www.cnblogs.com/lianshuiwuyi/p/12221913.html