关于java:protocol-buffer没那么难不信你看这篇

47次阅读

共计 3049 个字符,预计需要花费 8 分钟才能阅读完成。

简介

上一篇文章咱们对 google 的 protobuf 曾经有了一个根本的意识,并且可能应用相应的工具生成对应的代码了。然而对于.proto 文件的格局和具体反对的类型还不是很分明。明天本文将会带大家一探到底。

留神,本文介绍的协定是 proto3 版本的。

定义一个音讯

protobuf 中的主体被称为是 message,能够将其看做是咱们在程序中定义的类。咱们能够在.proto 文件中定义这个 message 对象,并且为其增加属性,如下所示:

syntax = "proto3";

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

上例的第一行指定了.proto 文件的协定类型,这里应用的是 proto3,也是最新版的协定,如果不指定,默认状况下是 proto2。

类型定义

这里咱们为 SearchRequest 对象,定义了三个属性,其类型别离是 String 和 int32。

String 和 int32 都是简略类型,protobuf 反对的简略类型如下:

protobuf 类型 阐明 对应的 java 类型
double 双精度浮点类型 double
float 浮点类型 float
int32 整型数字, 最好不示意正数 int
int64 整型数字,最好不示意正数 long
uint32 无符号整数 int
uint64 无符号整数 long
sint32 带符号整数 int
sint64 带符号整数 long
fixed32 四个字节的整数 int
fixed64 8 个字节的整数 long
sfixed32 4 个字节的带符号整数 int
sfixed64 8 个字节的带符号整数 long
bool 布尔类型 boolean
string 字符串 String
bytes 字节 ByteString

当然 protobuf 还反对简单的组合类型和枚举类型。

枚举类型在 protobuf 中用 enum 来示意,咱们来看一个枚举类型的定义:

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 也是枚举类型的默认值。

在枚举中,还能够定义具备雷同 value 的枚举类型,然而这样须要加上 allow_alias=true 的选项,如下所示:

message MyMessage1 {
  enum EnumAllowingAlias {
    option allow_alias = true;
    UNKNOWN = 0;
    STARTED = 1;
    RUNNING = 1;
  }
}
message MyMessage2 {
  enum EnumNotAllowingAlias {
    UNKNOWN = 0;
    STARTED = 1;
    // RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.
  }
}

在枚举类型中,如果咱们后续对某些枚举类型进行了删除,那么被删除的值可能会被后续的用户应用,这样就会造成潜在的代码隐患,为了解决这个问题,枚举提供了一个 reserved 的关键词,被这个关键词申明的枚举类型,就不会被后续应用,如下所示:

enum Foo {
  reserved 2, 15, 9 to 11, 40 to max;
  reserved "FOO", "BAR";
}

reserved 关键字也能够用在 message 的字段中,示意后续不要应用到这些字段,如下:

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
}

字段的值

咱们能够看到,每个 message 的字段都调配了一个值,每个字段的值在 message 中都是惟一的,这些值是用来定位在二进制音讯格局中的字段地位。所以一旦定义之后,不要随便批改。

要留神的是值 1 -15 在二进制中应用的 1 个字节来示意的,值 16-2047 须要应用 2 个字节来示意,所以通常将 1 -15 应用在最常见的字段和可能反复的字段,这样能够节约编码后的空间。

最小的值是 1,最大的值是 2 的 29 次方 -1,或者 536,870,911。这两头从 19000-19999 是保留数字,不能应用。

当音讯被编译之后,各个字段将会被转成为对应的类型,并且各个字段类型将会被赋予不同的初始值。

strings 的默认值是空字符串,bytes 的默认值是空 bytes,bools 的默认值是 false,数字类型的默认值是 0,枚举类型的默认值是枚举的第一个元素。

字段描述符

每个音讯的字段都能够有两种描述符,第一种叫做 singular,示意 message 中能够有 0 个或者 1 个这个字段,这是 proto3 中默认的定义形式。

第二种叫做 repeated,示意这个字段在 message 中是能够反复的,也就是说它代表的是一个汇合。

增加正文

在 proto 中的正文和 C ++ 的格调相似,能够应用:// 或者 // 的格调来正文,如下所示:

/* 这是一个正文. */

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // 页面的 number
  int32 result_per_page = 3;  // 每页的后果
}

嵌套类型

在一个 message 中还能够嵌入一个 message,如下所示:

message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}

在上例中,咱们在 SearchResponse 定义了一个 Result 类型,在 java 中,实际上能够将其看做是嵌套类。

如果心愿在 message 的定义类之外应用这个外部的 message,则能够通过 _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;
    }
  }
}

Map

如果想要在 proto 中定义 map,能够这样写:

map<key_type, value_type> map_field = N;

这里的 value_type 能够是除 map 之外的任意类型。留神 map 不能是 repeated。

map 中的数据的程序是不定的,咱们不能依赖存入的 map 程序来判断其取出的程序。

总结

以上就是 proto3 中定义申明文件该留神的事项了,大家在应用 protobuf 的时候要多加留神。

本文已收录于 http://www.flydean.com/02-protocolbuf-detail/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

正文完
 0