语言指导(proto3)

2019-11-01  本文已影响0人  红牛慕课_韩忠康

语言指导(proto3)

翻译自:https://developers.google.com/protocol-buffers/docs/proto3

关注 红牛慕课,发送 proto3 获取该文档的 PDF 版本。

本指导描述了如何使用 protocol buffer 语言来构建 protocol buffer 数据,包括 .proto 文件语法和如何基于该 .proto 文件生成数据访问类。本文是涵盖 protocol buffer 语言 proto3 版本的内容,若需要 proto2 版本的信息,请参考 Proto2 Language Guide

本文是语言指导——关于文中描述内容的分步示例,请参考所选编程语言的对应 tutorial (当前仅提供了 proto2,更多 proto3 的内容会持续更新)。

定义一个消息类型

我们先看一个简单示例。比如说我们想定义个关于搜索请求的消息,每个搜索请求包含一个查询字符串,一个特定的页码,和每页的结果数量。下面是用于定义消息类型的 .proto 文件:

syntax = "proto3";

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

指定字段类型

上面的例子中,全部字段都是标量类型:两个整型(page_numberresult_per_page)和一个字符串型(query)。同样,也可以指定复合类型的字段,包括枚举型和其他消息类型。

分配字段编号

正如你所见,消息中定义的每个字段都有一个唯一编号。字段编号用于在消息二进制格式中标识字段,同时要求消息一旦使用字段编号就不应该改变。注意一点 1 到 15 的字段编号需要用 1 个字节来编码,编码同时包括字段编号和字段类型( 获取更多信息请参考 Protocol Buffer Encoding )。16 到 2047 的字段变化使用 2 个字节。因此应将 1 到 15 的编号用在消息的常用字段上。注意应该为将来可能添加的常用字段预留字段编号。

最小的字段编号为 1,最大的为 2^29 - 1,或 536,870,911。注意不能使用 19000 到 19999 (FieldDescriptor::kFirstReservedNumberFieldDescriptor::kLastReservedNumber)的字段编号,因为是 protocol buffer 内部保留的——若在 .proto 文件中使用了这些预留的编号 protocol buffer 编译器会发出警告。同样也不能使用之前预留的字段编号。

指定字段规则

消息的字段可以是一下规则之一:

在 proto3 中,标量数值类型的重复字段默认会使用 packed 压缩编码。

更多关于 packed 压缩编码的信息请参考 Protocol Buffer Encoding

增加更多消息类型

单个 .proto 文件中可以定义多个消息类型。这在定义相关联的多个消息中很有用——例如要定义与搜索消息SearchRequest 相对应的回复消息 SearchResponse,则可以在同一个 .proto 文件中增加它的定义:

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

message SearchResponse {
 ...
}

增加注释

使用 C/C++ 风格的 ///* ... */ 语法在 .proto 文件添加注释。

/* 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.
}

保留字段

在采取彻底删除或注释掉某个字段的方式来更新消息类型时,将来其他用户再更新该消息类型时可能会重用这个字段编号。后面再加载该 .ptoto 的旧版本时会引发好多问题,例如数据损坏,隐私漏洞等。一个防止该问题发生的办法是将删除字段的编号(或字段名称,字段名称会导致在 JSON 序列化时产生问题)设置为保留项 reserved。protocol buffer 编译器在用户使用这些保留字段时会发出警告。

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

注意,不能在同一条 reserved 语句中同时使用字段编号和名称。

.proto 文件会生成什么?

当 protocol buffer 编译器作用于一个 .proto 文件时,编辑器会生成基于所选编程语言的关于 .proto 文件中描述消息类型的相关代码 ,包括对字段值的获取和设置,序列化消息用于输出流,和从输入流解析消息。

可以参考所选编程语言的教程了解更多 API 的信息。更多 API 详细信息,请参阅相关的 API reference

标量数据类型

消息标量字段可以是以下类型之一——下表列出了可以用在 .proto 文件中使用的类型,以及在生成代码中的相关类型:

.proto Type Notes C++ Type Java Type Python Type[2] Go Type Ruby Type C# Type PHP Type Dart Type
double double double float float64 Float double float double
float float float float float32 Float 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/long[3] uint32 Fixnum or Bignum (as required) uint integer int
fixed64 定长 8 字节。若值常大于2^56 则会比 uint64 更高效。 uint64 long[1] int/long[3] uint64 Bignum ulong integer/string[5] Int64
sfixed32 定长 4 字节。 int32 int int int32 Fixnum or Bignum (as required) int integer int
sfixed64 定长 8 字节。 int64 long int/long[3] int64 Bignum long integer/string[5] Int64
bool bool boolean bool bool TrueClass/FalseClass bool boolean bool
string 包含 UTF-8 和 ASCII 编码的字符串,长度不能超过 2^32 。 string String str/unicode[4] string String (UTF-8) string string String
bytes 可包含任意的字节序列但长度不能超过 2^32 。 string ByteString str []byte String (ASCII-8BIT) ByteString string List<int>

可以在 Protocol Buffer Encoding 中获取更多关于消息序列化时类型编码的相关信息。

[1] Java 中,无符号 32 位和 64 位整数使用它们对应的符号整数表示,第一个 bit 位仅是简单地存储在符号位中。

[2] 所有情况下,设置字段的值将执行类型检查以确保其有效。

[3] 64 位或无符号 32 位整数在解码时始终表示为 long,但如果在设置字段时给出 int,则可以为 int。在所有情况下,该值必须适合设置时的类型。见 [2]。

[4] Python 字符串在解码时表示为 unicode,但如果给出了 ASCII 字符串,则可以是 str(这条可能会发生变化)。

[5] Integer 用于 64 位机器,string 用于 32 位机器。

默认值

当解析消息时,若消息编码中没有包含某个元素,则相应的会使用该字段的默认值。默认值依据类型而不同:

对于可重复类型字段的默认值是空的( 通常是相应语言的一个空列表 )。

注意一下标量字段,在消息被解析后是不能区分字段是使用默认值(例如一个布尔型字段是否被设置为 false )赋值还是被设置为某个值的。例如你不能通过对布尔值等于 false 的判断来执行一个不希望在默认情况下执行的行为。同时还要注意若一个标量字段设置为默认的值,那么是不会被序列化以用于传输的。

查看 generated code guide 来获得更多关于编程语言生成代码的内容。

枚举

定义消息类型时,可能需要某字段值是一些预设值之一。例如当需要在 SearchRequest 消息类型中增加一个 corpus 字段, corpus 字段的值可以是 UNIVERSALWEBIMAGESLOCALNEWSPRODUCTSVIDEO。仅仅需要在消息类型中定义带有预设值常量的 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 的常量作为第一个元素。原因是:

将相同的枚举值分配给不同的枚举选项常量可以定义别名。要定义别名需要将 allow_alisa 选项设置为 true,否则 protocol 编译器当发现别名定义时会报错。

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 位整数的范围内。因为枚举值在传输时采用的是 varint 编码,同时负值无效因而不建议使用。可以如上面例子所示,将枚举定义在消息类型内,也可以将其定义外边——这样该枚举可以用在 .proto 文件中定义的任意的消息类型中以便重用。还可以使用 MessageType.EnumType 语法将枚举定义为消息字段的某一数据类型。

使用 protocol buffer 编译器编译 .proto 中的枚举时,对于 Java 或 C 会生成相应的枚举类型,对于 Python 会生成特定的 EnumDescriptor 类用于在运行时创建一组整型值符号常量即可。

反序列化时,未识别的枚举值会被保留在消息内,但如何表示取决于编程语言。若语言支持开放枚举类型允许范围外的值时,这些未识别的枚举值简单的以底层整型进行存储,就像 C++ 和 Go。若语言支持封闭枚举类型例如 Java,一种情况是使用特殊的访问器(译注:accessors)来访问底层的整型。无论哪种语言,序列化时的未识别枚举值都会被保留在序列化结果中。

更多所选语言中关于枚举的处理,请参考 generated code guide

保留值

在采取彻底删除或注释掉某个枚举值的方式来更新枚举类型时,将来其他用户再更新该枚举类型时可能会重用这个枚举数值。后面再加载该 .ptoto 的旧版本时会引发好多问题,例如数据损坏,隐私漏洞等。一个防止该问题发生的办法是将删除的枚举数值(或名称,名称会导致在 JSON 序列化时产生问题)设置为保留项 reserved。protocol buffer 编译器在用户使用这些特定数值时会发出警告。可以使用 max 关键字来指定保留值的范围到最大可能值。

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

注意不能在 reserved 语句中混用字段名称和数值。

使用其他消息类型

消息类型也可作为字段类型。例如,我们需要在 SearchResponse 消息中包含 Result 消息——想要做到这一点,可以将 Result 消息类型的定义放在同一个 .proto 文件中同时在 SearchResponse 消息中指定一个 Result 类型的字段:

message SearchResponse {
  repeated Result results = 1;
}

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

导入定义

前面的例子中,我们将 Result 消息定义在了与 SearchResponse 相同的文件中——但若我们需要作为字段类型使用的消息类型已经定义在其他的 .proto 文件中了呢?

可以通过导入操作来使用定义在其他 .proto 文件中的消息定义。在文件的顶部使用 import 语句完成导入其他 .proto 文件中的定义:

import "myproject/other_protos.proto";

默认情况下仅可以通过直接导入 .proto 文件来使用这些定义。然而有时会需要将 .proto 文件移动位置。可以通过在原始位置放置一个伪 .proto 文件使用 import public 概念来转发对新位置的导入,而不是在发生一点更改时就去更新全部对旧文件的导入位置。任何导入包含 import public 语句的 proto 文件就会对其中的 import public 依赖产生传递依赖。例如:

// new.proto
// 全部定义移动到该文件
// old.proto
// 这是在客户端中导入的伪文件
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// 可使用 old.proto 和 new.proto 中的定义,但不能使用 other.proto 中的定义

protocol 编译器会使用命令行参数 -I/--proto_path 所指定的目录集合中检索需要导入的文件。若没有指定,会在调用编译器的目录中检索。通常应该将 --proto_path 设置为项目的根目录同时在 import 语句中使用全限定名。

使用 proto2 类型

可以在 proto3 中导入 proto2 定义的消息类型,反之亦然。然而,proto2 中的枚举不能直接用在 proto3 语法中(但导入到 proto2 中 proto3 定义的枚举是可用的)。

嵌套类型

可以在一个消息类型中定义和使用另一个消息类型,如下例所示—— 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;
    }
  }
}

消息类型的更新

如果现有的消息类型不再满足您的所有需求——例如,需要扩展一个字段——同时还要继续使用已有代码,别慌! 在不破坏任何现有代码的情况下更新消息类型非常简单。仅仅遵循如下规则即可:

未知字段

未知字段是解析结构良好的 protocol buffer 已序列化数据中的未识别字段的表示方式。例如,当旧程序解析带有新字段的数据时,这些新字段就会成为旧程序的未知字段。

本来,proto3 在解析消息时总是会丢弃未知字段,但在 3.5 版本中重新引入了对未知字段的保留机制以用来兼容 proto2 的行为。在 3.5 或更高版本中,未知字段在解析时会被保留同时也会包含在序列化结果中。

Any 类型

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 类型的运行库正在开发中

若你已经熟悉了 proto2 语法,Any 类型的位于 extensions 部分。

Oneof

若一个含有多个字段的消息同时大多数情况下一次仅会设置一个字段,就可以使用 oneof 特性来强制该行为同时节约内存。

Oneof 字段除了全部字段位于 oneof 共享内存以及大多数情况下一次仅会设置一个字段外与常规字段类似。对任何oneof 成员的设置会自动清除其他成员。可以通过 case()WhichOneof() 方法来检测 oneof 中的哪个值被设置了,这个需要基于所选的编程语言。

使用 oneof

使用 oneof 关键字在 .proto 文件中定义 oneof,同时需要跟随一个 oneof 的名字,就像本例中的 test_oneof

message SampleMessage {
  oneof test_oneof {
    string name = 4;
    SubMessage sub_message = 9;
  }
}

然后将字段添加到 oneof 的定义中。可以增加任意类型的字段,但不能使用 repeated 字段。

在生成的代码中,oneof 字段和常规字段一致具有 getters 和 setters 。同时也会获得一个方法以用于检测哪个值被设置了。更多所选编程语言中关于 oneof 的 API 可以参考 API reference

Oneof 特性

SampleMessage message;
message.set_name("name");
CHECK(message.has_name());
message.mutable_sub_message();   // 会清理 name 字段
CHECK(!message.has_name());
SampleMessage message;
SubMessage* sub_message = message.mutable_sub_message();
message.set_name("name");      // 会删除 sub_message
sub_message->set_...            // 此处会崩溃
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 的成员。

标记重用问题

Map 映射表

若需要创建关联映射表作为定义的数据的一部分,protocol buffers 提供了方便的快捷语法:

map<key_type, value_type> map_field = N;

key_type 处可以是整型或字符串类型(其实是除了 float 和 bytes 类型外任意的标量类型)。注意枚举不是合法的 key_typevalue_type 是除了 map 外的任意类型。

例如,若需要创建每个项目与一个字符串 key 相关联的映射表,可以采用下面的定义:

map<string, Project> projects = 3;

生成的映射表 API 当前可用于全部支持 proto3 的编程语言。在 API reference 中可以获取更多关于映射表 API 的内容。

向后兼容问题

映射表语法与以下代码是对等的,因此 protocol buffers 的实现即使不支持映射表也可以正常处理数据:

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

repeated MapFieldEntry map_field = N;

任何支持映射表的 protocol buffers 实现都必须同时处理和接收上面代码的数据定义。

可以在 .proto 文件中使用 package 指示符来避免 protocol 消息类型间的命名冲突。

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

这样在定义消息的字段类型时就可以使用包指示符来完成:

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

包指示符的处理方式是基于编程语言的:

包和名称解析

protocol buffer 中类型名称解析的工作机制类似于 C++ :先搜索最内层作用域,然后是次内层,以此类推,每个包被认为是其外部包的内层。前导点(例如,.foo.bar.Baz)表示从最外层作用域开始。

protocol buffer 编译器会解析导入的 .proto 文件中的全部类型名称。基于编程语言生成的代码也知道如何去引用每种类型,即使编程语言有不同的作用域规则。

定义服务

若要在 RPC (Remote Procedure Call,远程过程调用)系统中使用我们定义的消息类型,则可在 .proto 文件中定义这个 RPC 服务接口,同时 protocol buffer 编译器会基于所选编程语言生成该服务接口代码。例如,若需要定义一个含有可以接收 SearchRequest 消息并返回 SearchResponse 消息方法的 RPC 服务,可以在 .proto 文件中使用如下代码定义:

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

最直接使用 protocal buffer 的 RPC 系统是 gRPC :一款 Google 开源,语言和平台无关的 RPC 系统。gRPC 对 protocol buffer 的支持非常好同时允许使用特定的 protocol buffer 编译器插件来基于 .proto 文件生成相关的代码。

若不想使用 gRPC,同样可以在自己的 RPC 实现上使用 protocol buffer。可以在 Proto2 Language Guide 处获得更多关于这方面的信息。

同样也有大量可用的第三方使用 protocol buffer 的项目。对于我们了解的相关项目列表,请参考 third-party add-ons wiki page

JSON 映射

Proto3 支持 JSON 的规范编码,这使得系统间共享数据变得更加容易。下表中,将逐类型地描述这些编码。

若 JSON 编码中不存在某个值或者值为 null,当将其解析为 protocol buffer 时会解析为合适的默认值。若 procol buffer 中使用的是字段的默认值,则默认情况下 JSON 编码会忽略该字段以便于节省空间。实现上应该提供一个选项以用来将具有默认值的字段生成在 JSON 编码中。

proto3 JSON JSON 示例 说明
message object {"fooBar": v, "g": null,…} 生成 JSON 对象。消息字段名映射为对象的 lowerCamelCase(译著:小驼峰) 的 key。若指定了 json_name 选项,则使用该选项值作为 key。解析器同时支持 lowerCamelCase 名称(或 json_name 指定名称)和原始 proto 字段名称。全部类型都支持 null 值,是当做对应类型的默认值来对待的。
enum string "FOO_BAR" 使用 proto 中指定的枚举值的名称。解析器同时接受枚举名称和整数值。
map<K,V> object `{"k": v, …} 所有的 key 被转换为字符串类型。
repeated V array [v, …] null 被解释为空列表 []。
bool true, false true, false
string string "Hello World!"
bytes base64 string "YWJjMTIzIT8kKiYoKSctPUB+" JSON 值是使用标准边界 base64 编码的字符串。不论标准或 URL 安全还是携带边界与否的 base64 编码都支持。
int32, fixed32, uint32 number 1, -10, 0 JSON 值是 10 进制数值。数值或字符串都可以支持。
int64, fixed64, uint64 string "1", "-10" JSON 值是 10 进制字符串。数值或字符串都支持。
float, double number 1.1, -10.0, 0, "NaN","Infinity" JSON 值是数值或特定的字符串之一:"NaN","Infinity" 和 "-Infinity" 。数值和字符串都支持。指数表示法同样支持。
Any object {"@type": "url", "f": v, … } 若 Any 类型包含特定的 JSON 映射值,则会被转换为下面的形式: {"@type": xxx, "value": yyy}。否则,会被转换到一个对象中,同时会插入一个 "@type" 元素用以指明实际的类型。
Timestamp string "1972-01-01T10:00:20.021Z" 采用 RFC 3339 格式,其中生成的输出总是 Z规范的,并使用 0、3、6 或 9 位小数。除 “Z” 以外的偏移量也可以。
Duration string "1.000340012s", "1s" 根据所需的精度,生成的输出可能会包含 0、3、6 或 9 位小数,以 “s” 为后缀。只要满足纳秒精度和后缀 “s” 的要求,任何小数(包括没有)都可以接受。
Struct object { … } 任意 JSON 对象。参见 struct.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 Any JSON value
NullValue null JSON null
Empty object {} 空 JSON 对象

JSON 选项

proto3 的 JSON 实现可以包含如下的选项:

选项

.proto 文件中的单个声明可以被一组选项来设置。选项不是用来更改声明的含义,但会影响在特定上下文下的处理方式。完整的选项列表定义在 google/protobuf/descriptor.proto 中。

有些选项是文件级的,意味着可以卸载顶级作用域,而不是在消息、枚举、或服务的定义中。有些选项是消息级的,意味着需写在消息的定义中。有些选项是字段级的,意味着需要写在字段的定义内。选项还可以写在枚举类型,枚举值,服务类型,和服务方法上;然而,目前还没有任何可用于以上位置的选项。

下面是几个最常用的选项:

option java_package = "com.example.foo";
option java_multiple_files = true;
option java_outer_classname = "Ponycopter";
option optimize_for = CODE_SIZE;    
int32 old_field = 6 [deprecated=true];

自定义选项

protocol buffer 还允许使用自定义选项。大多数人都不需要此高级功能。若确认要使用自定义选项,请参阅 Proto2 Language Guide 了解详细信息。注意使用 extensions 来创建自定义选项,只允许用于 proto3 中。

生成自定义类

若要生成操作 .proto 文件中定义的消息类型的 Java、Python、C++、Go、Ruby、Objective-C 或 C# 代码,需要对 .proto 文件运行 protocol buffer 编译器 protoc。若还没有安装编译器,请 download the package 并依据 README 完成安装。对于 Go ,还需要为编译器安装特定的代码生成器插件:可使用 GitHub 上的 golang/protobuf 库。

Protocol buffer 编译器的调用方式如下:

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

作为额外的便利,若 DST_DIR 以 .zip.jar 结尾,编译器将会写入给定名称的 ZIP 格式压缩文件,.jar 还将根据 Java JAR 的要求提供一个 manifest 文件。请注意,若输出文件已经存在,它将被覆盖;编译器还不够智能,无法将文件添加到现有的存档中。

关注 红牛慕课,发送 proto3 获取该文档的 PDF 版本。


subscribe-proto3.png
上一篇下一篇

猜你喜欢

热点阅读