protoc go插件编写之三 (自定义选项)

选项 [options]

.proto文件中的各个声明【message呀,service呀等等】可以使用许多选项进行注释。选项不会改变声明的整体含义,但可能会影响在特定上下文中处理声明的方式。

可用选项的完整列表在google/protobuf/descriptor.proto中定义。

一些选项是文件级别的, 只能在顶级内容区, 而不能在任何 message, 或enum,或service中定义。一些是消息级别的, 只能定义在message内, 一些是字段级别的,只能定义在filed内。选项也可以写在枚举类型,枚举值,字段,服务类型和服务方法中;但是,目前尚无任何针对这些选项的有用选项。

  • file level options

    • 例1: go_package 定义了go中,包的导入路径(其包引用此包的路径,功能和 命令中的-M类似,但是优先级低于-M参数);
    options go_package = github.com/kekek/test; 
    
    • 例2: optimize_for, 可以设置为 SPEED, CODE_SIZE, 或 LITE_RUNTIME, 只对C++ 或者java起作用
    option optimize_for = CODE_SIZE;
    
  • message level options

    例1:message_set_wire_format, 如果设置为true,则消息使用另一种二进制格式,旨在与Google内部使用的旧格式MessageSet兼容。 Google外部的用户可能永远不需要使用此选项。

    message Foo {
        option message_set_wire_format = true;
        extensions 4 to max;
    }
    
    
  • filed options

    例1: packed, 如果在基本数字类型的重复字段上设置为true,则使用更紧凑的编码。使用此选项没有任何弊端。但是,请注意,在版本2.3.0之前,解析器在不期望收到打包数据时将忽略该数据。因此,不可能在不破坏电线兼容性的情况下将现有字段更改为打包格式。在2.3.0及更高版本中,此更改是安全的,因为可打包字段的解析器将始终接受两种格式,但是如果必须使用旧的protobuf版本处理旧程序,请务必小心。

    repeated int32 samples = 4 [packed=true];
    

    例2: deprecated, 如果设置为true,则表明该字段已弃用,并且不应由新代码使用。在大多数语言中,这没有实际效果。在Java中,这成为@Deprecated批注。

    optional int32 old_field = 6 [deprecated=true];
    

自定义选项 [Custom Options]

Protocol Buffers 允许自定义选项,这是大多数人用不到的功能, 且自定义选项仅支持proto3。

由于"选项" 是由 google/protobuf/descriptor.proto中定义的消息定义的(例如FileOptions或FieldOptions),因此自定义选项仅是扩展这些消息而已。

例如:

import "google/protobuf/descriptor.proto";

extend google.protobuf.MessageOptions {
  optional string my_option = 51234;
}

message MyMessage {
  option (my_option) = "Hello world!";
}

在这里,我们通过扩展MessageOptions定义了一个新的消息级选项。当我们使用选项时,选项名称必须用括号括起来以表明它是扩展名。我们现在可以像这样在C++中读取my_option的值:


string value = MyMessage::descriptor()->options().GetExtension(my_option);

在这里,MyMessage :: descriptor()-> options()返回MyMessage的MessageOptions协议消息。从中读取自定义选项就像读取任何其他扩展名一样。

在protobuf protocol 中,可以为每种构造定义自定义选项。这是使用各种选项的示例:

import "google/protobuf/descriptor.proto";

extend google.protobuf.FileOptions {
  optional string my_file_option = 50000;
}
extend google.protobuf.MessageOptions {
  optional int32 my_message_option = 50001;
}
extend google.protobuf.FieldOptions {
  optional float my_field_option = 50002;
}
extend google.protobuf.OneofOptions {
  optional int64 my_oneof_option = 50003;
}
extend google.protobuf.EnumOptions {
  optional bool my_enum_option = 50004;
}
extend google.protobuf.EnumValueOptions {
  optional uint32 my_enum_value_option = 50005;
}
extend google.protobuf.ServiceOptions {
  optional MyEnum my_service_option = 50006;
}
extend google.protobuf.MethodOptions {
  optional MyMessage my_method_option = 50007;
}

option (my_file_option) = "Hello world!";

message MyMessage {
  option (my_message_option) = 1234;

  optional int32 foo = 1 [(my_field_option) = 4.5];
  optional string bar = 2;
  oneof qux {
    option (my_oneof_option) = 42;

    string quux = 3;
  }
}

enum MyEnum {
  option (my_enum_option) = true;

  FOO = 1 [(my_enum_value_option) = 321];
  BAR = 2;
}

message RequestType {}
message ResponseType {}

service MyService {
  option (my_service_option) = FOO;

  rpc MyMethod(RequestType) returns(ResponseType) {
    // Note:  my_method_option has type MyMessage.  We can set each field
    //   within it using a separate "option" line.
    option (my_method_option).foo = 567;
    option (my_method_option).bar = "Some string";
  }
}


请注意,如果要在自定义选项之外的其他程序包中使用自定义选项,则必须在选项名称前加上程序包名称,就像键入类型名称一样。例如:

  • foo.proto
import "google/protobuf/descriptor.proto";
package foo;
extend google.protobuf.MessageOptions {
  optional string my_option = 51234;
}
  • bar.proto
import "foo.proto";
package bar;
message MyMessage {
  option (foo.my_option) = "Hello world!";
}

最后一件事:由于自定义选项是扩展名,因此必须像其他任何字段或扩展名一样为它们分配字段编号。在上面的示例中,我们使用了范围为50000-99999的字段号。此范围保留供各个组织内部使用,因此您可以在内部应用程序中自由使用此范围内的数字。但是,如果您打算在公共应用程序中使用自定义选项,那么确保您的字段编号在全球范围内是唯一的,这一点很重要。要获取全局唯一的字段号,请发送请求以将条目添加到protobuf全局扩展注册表中。通常,您只需要一个分机号码。您可以通过将多个选项放在一个子消息中来声明多个仅具有一个分机号的选项:

message FooOptions {
  optional int32 opt1 = 1;
  optional string opt2 = 2;
}

extend google.protobuf.FieldOptions {
  optional FooOptions foo_options = 1234;
}

// usage:
message Bar {
  optional int32 a = 1 [(foo_options).opt1 = 123, (foo_options).opt2 = "baz"];
  // alternative aggregate syntax (uses TextFormat):
  optional int32 b = 2 [(foo_options) = { opt1: 123 opt2: "baz" }];
}

另外,请注意,每个选项类型(文件级,消息级,字段级等)都有自己的数字空间,例如您可以使用相同的编号声明FieldOptions和MessageOptions的扩展名。

参考文献:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容