Protocol Buffers V2 版本语法要点记录

Tags: protobuf 

本篇目录

说明

Protocol Buffers V3 版本和 V2 相比有了比较大的变化,删除了 V2 的一些特性。 这是源自 Language Guide(proto2) 的 V2 版本语法要点,建议使用 V3 版本 Protocol Buffers V3 协议语法要点记录

IntelliJ IDEA 未识别 protobuf 的 import 的解决方法

安装了 protobuf 插件,IntelliJ IDEA 还是不能识别 protobuf 的 import:

IntelliJ IDEA 不能识别 protobuf 的 import

Protocol Buffer imports not recognized in Intellij 给出的方法有效:

解决protobuf import导入问题

代码生成方法

代码生成命令,IMPORT_PATH 指定查找 proto 文件的路径:

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto

package 包名声明

package 声明文件的包名:

package foo.bar;
message Open { ... }

其它 package 通过 package name 引用包内的定义:

message Foo {
  required foo.bar.Open open = 1;
}

package 以及定义引用

用 import 导入目标文件:

import "myproject/other_protos.proto";

import public

A 文件通过 import public 引用 B 文件后,只需引用 A 文件就可以直接使用 B 文件中的定义。

import public 不支持 java,生成 java 代码时不能使用该功能。

// 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

proto2 可以引用 proto3 的定义,proto3 不能引用 proto2 中的 enum。

引用嵌套定义的 message

message SearchResponse {
  message Result {
    required string url = 1;
    optional string title = 2;
    repeated string snippets = 3;
  }
  repeated Result result = 1;
}

// 引用 SearchResponse 中定义的 Result
message SomeOtherMessage {
  optional SearchResponse.Result result = 1;
}

option 配置项

option 分为file-levelmessage-levelfield-level 三种级别,分别在不同的位置使用。

google/protobuf/descriptor.proto 介绍了所有 option 的用法。

file-level options

file 级别的 options 用法:

option go_package = "google.golang.org/protobuf/types/descriptorpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "DescriptorProtos";
option csharp_namespace = "Google.Protobuf.Reflection";
option objc_class_prefix = "GPB";
option cc_enable_arenas = true;

// descriptor.proto must be optimized for speed because reflection-based
// algorithms don't work during bootstrapping.
option optimize_for = SPEED;

message-level options

message 级别的 option 用法:

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

field-level options

field 级别的 option 用法:

repeated int32 samples = 4 [packed = true];
optional int32 old_field = 6 [deprecated=true];

自定义 options

可以通过扩展 google.protobuf.XXXOptions 增加自定义的 option:

import "google/protobuf/descriptor.proto";

extend google.protobuf.MessageOptions {   
  optional string my_option = 51234;       // 自定义 message-level option
}

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

rpc 接口定义

rpc 接口定义方法:

service SearchService {
  rpc Search(SearchRequest) returns (SearchResponse);
}

message 定义

message SearchRequest {
  required string query = 1;
  optional int32 page_number = 2;
  optional int32 result_per_page = 3;
}

field 的编号:

  1. field 编号发布后不能再更改
  2. 编号 1~15 用一个字节描述,编号 16~2047 用两个字节
  3. 编号最小值是 1,最大 2^29-1
  4. 19000~19999 用于内部实现,不能使用

注意事项 :

  1. 不要修改 field 的编号
  2. 新增的 field 使用 optional 或者 repeated
  3. 非 required 类型的 field 可以删除,但是 field num 不能再次分配给其它 field

extensions 预留用于扩展的 field 编号

extensions 用来预留可以被第三方使用的字段编号:

message Foo {
  // ...
  extensions 100 to 199;
}

// 另一个 proto 文件,扩展 Foo:
extend Foo {
  optional int32 bar = 126;     // 扩展的 field 不能是 oneof、map
}

标准数值类型 Scalar Value Types

支持的类型:Scalar Value Types

double
float
int32
int64
uint32
uint64
sint32: 对负数的编码更高效
sint64: 对负数的编码更高效
fixed32:永远 4 字节,对应大于 2^28 的数值编码更高效
fixed64:永远 8 字节,对应大于 2^56 的数值编码更高效
sfixed32: 永远 4 字节
sfixed64: 永远 8 字节
bool:
string:UTF-8 或者 7-bit ASCII Text
bytes:

repeated

field 类型:

  1. repeated 修饰的 field,会保证参数顺序
  2. repeated 修饰的数值类型,因为历史原因默认不采用高效编码,启用高效编码需要明确指示 [packed = true]。
repeated int32 samples = 4 [packed = true];

reserved

用 reserved 避免曾经使用过后来被删除的 field 的编号被再次使用:

message Foo {
  reserved 2, 15, 9 to 11;     // 使用编号
  reserved "foo", "bar";       // 或者名称,但是编号和名称不同同时混用
}

enum Foo {
  reserved 2, 15, 9 to 11, 40 to max;   // 用 max 表示最大值
  reserved "FOO", "BAR";
}

default option

proto3删除了该语法

用 default 设置 optional field 缺失时的默认数值,如果未设置 default 使用类型默认值:

optional int32 result_per_page = 3 [default = 10];

map 类型

map<key_type, value_type> map_field = N;

map 的注意事项:

  1. map 的 key 不能是 float 和 bytes
  2. map 不能用 repeated, optional, required 修饰
  3. map 内的 key-value 顺序是不保证的

map 等价于下面的定义:

message MapFieldEntry {
  optional key_type key = 1;
  optional value_type value = 2;
}

repeated MapFieldEntry map_field = N;

enum 类型

enum 使用 32bit,使用 varint 编码,对负数编码效率低,不建议使用负数。

enum 可以独立定义,也可以在 message 内部定义:

message SearchRequest {
  required string query = 1;
  optional int32 page_number = 2;
  optional int32 result_per_page = 3 [default = 10];
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  optional Corpus corpus = 4 [default = UNIVERSAL];
}

支持别名:

enum EnumAllowingAlias {
  // 枚举值存在重复时,如果不指定 allow_alias=true,会报错
  option allow_alias = true;
  EAA_UNSPECIFIED = 0;
  EAA_STARTED = 1;
  EAA_RUNNING = 1;
  EAA_FINISHED = 2;
}

## oneof 类型

oneof 中的 field 不能直接使用 requriedoptionalrepeated,可以使用内部使用了这些修饰符的 message

```proto
message SampleMessage {
  oneof test_oneof {
     // oneof 内不能使用 requried、optional、repeated 修饰
     string name = 4;  
     // SubMessage 内部的 field 可以用 requried、optional、repeated 修饰 
     SubMessage sub_message = 9;   
  }
}

group

group 不建议使用了,用嵌套定义替代 :

message SearchResponse {
  repeated group Result = 1 {      // group 不建议使用了
    required string url = 2;
    optional string title = 3;
    repeated string snippets = 4;
  }
}

参考

  1. 李佶澳的博客
  2. Protocol Buffer imports not recognized in Intellij
  3. PLanguage Guide(proto2)
  4. google/protobuf/descriptor.proto
  5. Language Guide (proto3)
  6. why messge type remove ‘required,optional’?

推荐阅读

Copyright @2011-2019 All rights reserved. 转载请添加原文连接,合作请加微信lijiaocn或者发送邮件: [email protected],备注网站合作

友情链接:  系统软件  程序语言  运营经验  水库文集  网络课程  微信网文  发现知识星球