前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Protobuf生成Go代码指南

Protobuf生成Go代码指南

作者头像
KevinYan
发布2019-10-13 17:26:14
5.4K0
发布2019-10-13 17:26:14
举报
文章被收录于专栏:网管叨bi叨网管叨bi叨

这个教程中将会描述protocol buffer编译器通过给定的 .proto会编译生成什么Go代码。教程针对的是proto3版本的protobuf。在阅读之前确保你已经阅读过Protobuf语言指南

编译器调用

Protobuf核心的工具集是C++语言开发的,官方的protoc编译器中并不支持Go语言,需要安装一个插件才能生成Go代码。用如下命令安装:

代码语言:javascript
复制
$ go get github.com/golang/protobuf/protoc-gen-go

提供了一个 protoc-gen-go二进制文件,当编译器调用时传递了 --go_out命令行标志时 protoc就会使用它。--go_out告诉编译器把Go源代码写到哪里。编译器会为每个 .proto文件生成一个单独的源代码文件。

输出文件的名称是通过获取.proto文件的名称并进行两处更改来计算的:

  • 生成文件的扩展名是 .pb.go。比如说 player_record.proto编译后会得到 player_record.pb.go
  • proto路径(使用 --proto_path-I命令行标志指定)将替换为输出路径(使用 --go_out标志指定)。

当你运行如下编译命令时:

代码语言:javascript
复制
protoc --proto_path=src --go_out=build/gen src/foo.proto src/bar/baz.proto

编译器会读取文件 src/foo.protosrc/bar/baz.proto,这将会生成两个输出文件 build/gen/foo.pb.gobuild/gen/bar/baz.pb.go

如果有必要,编译器会自动生成 build/gen/bar目录,但是他不能创建 build或者 build/gen目录,这两个必须是已经存在的目录。

如果一个 .proto文件中有包声明,生成的源代码将会使用它来作为Go的包名,如果 .proto的包名中有 . 在Go包名中会将 .转换为 _。举例来说 proto包名 example.high_score将会生成Go包名 example_high_score

.proto文件中可以使用option go_package指令来覆盖上面默认生成Go包名的规则。比如说包含如下指令的一个 .proto文件

代码语言:javascript
复制
package example.high_score;option go_package = "hs";

生成的Go源代码的包名是 hs

如果一个 .proto文件中不包含package声明,生成的源代码将会使用 .proto文件的文件名(去掉扩展名)作为Go包名, .会被首先转换为 _。举例来说一个名为 high.score.proto不包含pack声明的文件将会生成文件 high.score.pb.go,他的Go包名是 high_score

消息

一个简单的消息声明:

代码语言:javascript
复制
message Foo {}

protocol buffer编译器将会生成一个名为 Foo的结构体,实现了 proto.Message接口的 Foo类型的指针

代码语言:javascript
复制
type Foo struct {}
// 重置proto为默认值func (m *Foo) Reset()         { *m = Foo{} }
// String 返回proto的字符串表示func (m *Foo) String() string { return proto.CompactTextString(m) }
// ProtoMessage作为一个tag 确保其他人不会意外的实现// proto.Message 接口.func (*Foo) ProtoMessage()    {}

内嵌的消息

一个message可以声明在其他message的内部。比如说:

代码语言:javascript
复制
message Foo {  message Bar {  }}

这种情况,编译器会生成两个结构体:FooFoo_Bar

预定义消息类型

Protobufs带有一组预定义的消息,称为众所周知的类型(WKT)。这些类型可以用于与其他服务的互操作性,或者仅仅因为它们简洁地表示了常见的有用模式。例如,Struct消息表示任意C样式结构的格式。

WKT的预生成Go代码作为Go protobuf库的一部分进行分发,如果message中使用了WKT,则生成的消息的Go代码会引用此代码。例如,给出如下消息:

代码语言:javascript
复制
import "google/protobuf/struct.proto"import "google/protobuf/timestamp.proto"
message NamedStruct {  string name = 1;  google.protobuf.Struct definition = 2;  google.protobuf.Timestamp last_modified = 3;}

生成的Go代码将会像下面这样:

代码语言:javascript
复制
import google_protobuf "github.com/golang/protobuf/ptypes/struct"import google_protobuf1 "github.com/golang/protobuf/ptypes/timestamp"
...
type NamedStruct struct {   Name         string   Definition   *google_protobuf.Struct   LastModified *google_protobuf1.Timestamp}

一般来说,您不需要将这些类型直接导入代码中。但是,如果需要直接引用其中一种类型,只需导入github.com/golang/protobuf/ptypes/[TYPE]包,并正常使用该类型。

字段

编译器会为每个在message中定义的字段生成一个Go结构体的字段,字段的确切性质取决于它的类型以及它是 singularrepeatedmap还是 oneof字段。

注意生成的Go结构体的字段将始终使用驼峰命名,即使在 .proto文件中消息字段用的是小写加下划线(应该这样)。大小写转换的原理如下:

  • 首字母会大些,如果message中字段的第一个字符是 _,它将被替换为X。
  • 如果内部下划线后跟小写字母,则删除下划线,并将后面跟随的字母大写。

因此,proto字段 foo_bar_baz在Go中变成 FooBarBaz_my_field_name_2变为 XMyFieldName_2

单一标量字段

对于字段定义:

代码语言:javascript
复制
int32 foo = 1;

编译器将生成一个带有名为Foo的int32字段和一个访问器方法GetFoo()的结构,该方法返回Foo中的int32值或该字段的零值(如果字段未设置(数值型零值为0,字符串为空字符串))。

单一message字段

给出如下消息类型

代码语言:javascript
复制
message Bar {}

对于一个有 Bar类型字段的消息:

代码语言:javascript
复制
// proto3message Baz {  Bar foo = 1;}

编译器将会生成一个Go结构体

代码语言:javascript
复制
type Baz struct {        Foo *Bar}

消息类型的字段可以设置为nil,这意味着该字段未设置,有效清除该字段。这不等同于将值设置为消息结构体的“空”实例。

编译器还生成一个 func(m*Baz)GetFoo()*Bar辅助函数。这让不在中间检查nil值进行链式调用成为可能。

可重复字段

每个重复的字段在Go中的结构中生成一个T类型的slice,其中T是字段的元素类型。对于带有重复字段的此消息:

代码语言:javascript
复制
message Baz {  repeated Bar foo = 1;}

编译器会生成如下结构体:

代码语言:javascript
复制
type Baz struct {        Foo  []*Bar}

同样,对于字段定义 repeated bytes foo=1;编译器将会生成一个带有类型为 [][]byte名为 Foo的字段的Go结构体。对于可重复的枚举 repeatedMyEnumbar=2;,编译器会生成带有类型为 []MyEnum名为 Bar的字段的Go结构体。

映射字段

每个映射字段会在Go的结构体中生成一个 map[TKey]TValue类型的字段,其中 TKey是字段的键类型 TValue是字段的值类型。对于下面这个消息定义:

代码语言:javascript
复制
message Bar {}
message Baz {  map<string, Bar> foo = 1;}

编译器生成Go结构体

代码语言:javascript
复制
type Baz struct {        Foo map[string]*Bar}

枚举

给出如下枚举

代码语言:javascript
复制
message SearchRequest {  enum Corpus {    UNIVERSAL = 0;    WEB = 1;    IMAGES = 2;    LOCAL = 3;    NEWS = 4;    PRODUCTS = 5;    VIDEO = 6;  }  Corpus corpus = 1;  ...}

编译器将会生成一个枚举类型和一系列该类型的常量。

对于消息中的枚举(像上面那样),类型名字以消息名开头

代码语言:javascript
复制
type SearchRequest_Corpus int32

对于包级别的枚举:

代码语言:javascript
复制
// .protoenum Foo {  DEFAULT_BAR = 0;  BAR_BELLS = 1;  BAR_B_CUE = 2;}

Go 中的类型不会对proto中的枚举名称进行修改:

代码语言:javascript
复制
type Foo int32

此类型具有 String()方法,该方法返回给定值的名称。

Enum()方法使用给定值初始化新分配的内存并返回相应的指针:

代码语言:javascript
复制
func (Foo) Enum() *Foo

编译器为枚举中的每个值生成一个常量。对于消息中的枚举,常量以消息的名称开头:

代码语言:javascript
复制
const (        SearchRequest_UNIVERSAL SearchRequest_Corpus = 0        SearchRequest_WEB       SearchRequest_Corpus = 1        SearchRequest_IMAGES    SearchRequest_Corpus = 2        SearchRequest_LOCAL     SearchRequest_Corpus = 3        SearchRequest_NEWS      SearchRequest_Corpus = 4        SearchRequest_PRODUCTS  SearchRequest_Corpus = 5        SearchRequest_VIDEO     SearchRequest_Corpus = 6)

对于包级别的枚举,常量以枚举名称开头:

代码语言:javascript
复制
const (        Foo_DEFAULT_BAR Foo = 0        Foo_BAR_BELLS   Foo = 1        Foo_BAR_B_CUE   Foo = 2)

protobuf编译器还生成从整数值到字符串名称的映射以及从名称到值的映射:

代码语言:javascript
复制
var Foo_name = map[int32]string{        0: "DEFAULT_BAR",        1: "BAR_BELLS",        2: "BAR_B_CUE",}var Foo_value = map[string]int32{        "DEFAULT_BAR": 0,        "BAR_BELLS":   1,        "BAR_B_CUE":   2,}

请注意, .proto语言允许多个枚举符号具有相同的数值。具有相同数值的符号是同义词。这些在Go中以完全相同的方式表示,多个名称对应于相同的数值。反向映射包含数字值的单个条目,数值映射到出现在 proto文件中首先出现的名称。

服务

默认情况下,Go代码生成器不会为服务生成输出。如果您启用gRPC插件(请参阅gRPC Go快速入门指南),则会生成代码以支持gRPC。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 网管叨bi叨 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 编译器调用
  • 消息
    • 内嵌的消息
      • 预定义消息类型
      • 字段
        • 单一标量字段
          • 单一message字段
            • 可重复字段
              • 映射字段
              • 枚举
              • 服务
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档