此页面准确描述了protocol buffers
编译器为任何给定协议定义生成的Go代码。proto2
和proto3
生成的代码之间的任何差异都会突出显示。请注意,这些差异在本文档中描述的生成代码中,而不是基本API,两个版本中的API相同。在阅读本文档之前,应该阅读proto2
语言指南和或proto3
语言指南。
protocol buffers
编译器需要一个插件生成Go代码。执行如下命令来安装:
go get github.com/golang/protobuf/protoc-gen-go
在命令行中使用--go_out
标志时,由protoc
编译器调用protoc-gen-go
二进制文件。--go_out
标志告诉编译器Go源文件输出位置。编译器为每个.proto
文件创建单个源文件。
输出文件的名称是根据.proto
文件的名称得来的,在两个地方可以更改:
.proto
)替换为.pb.go
。例如,名为player_record.proto
的文件会生成一个名为player_record.pb.go
的输出文件。--proto_path
或-I
命令行标志指定)将替换为输出路径(使用--go_out
标志指定)。当像下面这样运行proto
编译器时:
protoc --proto_path=src --go_out=build/gen src/foo.proto src/bar/baz.proto
编译器将读取src/foo.proto
和src/bar/baz.proto
文件。它产生两个输出文件:build/gen/foo.pb.go
和build/gen/bar/baz.pb.go
。
如有必要,编译器会自动创建build/gen/bar
目录,但不会创建build
或build/gen
目录,所以这两个目录必须已经存在。
如果.proto
文件中有package
声明,则生成的代码使用proto的package
声明作为其Go包名称,其中将"."
转换为"_"
。例如,example.high_score
的proto的package
名称生成的Go包名称为example_high_score
。
可以使用.proto
文件中的go_package
选项覆盖特定.proto
默认生成的包。如下.proto
文件,生成的Go包名为hs
。
package example.high_score; option go_package = "hs";
否则,如果.proto
文件不包含package
声明,则生成的代码使用文件名(减去扩展名)作为Go包名称,并将"."
转换为"_“
。例如,一个名为high.score.proto
的proto包,其中的没有package
声明,将生成在high_score
包中生成一个名为high.score.pb.go
的文件。
给出一个简单的消息声明:
message Foo {}
protocol buffers
编译器生成一个名为Foo
的结构和一个实现了Message*Foo
的接口。有关详细信息,请参阅内联注释。
type Foo struct { } // Reset sets the proto's state to default values. func (m *Foo) Reset() { *m = Foo{} } // String returns a string representation of the proto. func (m *Foo) String() string { return proto.CompactTextString(m) } // ProtoMessage acts as a tag to make sure no one accidentally implements the // proto.Message interface. func (*Foo) ProtoMessage() {}
请注意,所有这些成员始终存在, optimize_for
选项不会影响Go代码生成器的输出。
可以在一个条消息中嵌套声明另一个消息。例如:
message Foo { message Bar { } }
在这种情况下,编译器生成两个结构:Foo
和Foo_Bar
。
Protobufs
带有一组预定义的消息,称为知名类型(WKT
)。这些类型可以用于与其他服务的互操作性,或者仅仅因为它们简洁地表示常见的有用模式。 例如,Struct
消息表示任意C风格的结构体。
WKT
的预生成Go代码作为Go protobuf
库的一部分进行分发,如果使用WKT
,则生成的消息的Go代码会引用此代码。例如,给出如下消息:
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代码如下所示:
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]
包,并正常使用该类型。
protocol buffers
编译器为消息中定义的每个字段生成结构体的字段。该字段的确切性质取决于它的类型以及它是否是singular
, repeated
, map
或者oneof
字段。
请注意,生成的Go字段名称始终使用大驼峰式(CamelCase)命名,即使.proto
文件中的字段名称使用带有下划线的小写(应该如此)。大小写转换的工作原理如下:
X
。因此:
foo_bar_baz
在Go中变为FooBarBaz
_my_field_name_2
在GO中变为XMyFieldName_2
对于以下任一字段定义:
optional int32 foo = 1; required int32 foo = 1;
编译器生成一个结构,其中包含一个名为Foo
的*int32
字段和一个访问器方法GetFoo()
它返回Foo
中的int32
值或默认值(如果该字段未设置)。如果未显式设置默认值,则使用该类型的零值(0表示数字,空字符串表示字符串)。
对于其他标量字段类型(包括bool
,bytes
和string
),*int32
将根据[标量值类型]..(/Protocol-Buffers/02-proto3指南.md)表替换为相应的Go类型。
对于此字段定义:
int32 foo = 1;
编译器将生成一个带有名为Foo
的int32
字段和一个访问器方法GetFoo()
的结构体,该方法返回Foo
中的int32
值或该字段的零值,如果字段未设置(0表示数字,字符串为空字符串)。
对于其他标量字段类型(包括bool
,bytes
和string
),根据标量值类型将int32
替换为相应的Go类型。proto中的未设置值将表示为该类型的零值(0表示数字,空字符串表示字符串)。
给定消息类型:
message Bar {}
对于带有Bar字段的消息:
// proto2 message Baz { optional Bar foo = 1; // The generated code is the same result if required instead of optional. } // proto3 message Baz { Bar foo = 1; }
编译器将生成Go结构:
type Baz struct { Foo *Bar }
消息字段可以设置为nil
,这意味着该字段未设置,有效的清除该字段。这不等同于将值设置为消息结构体的“空”实例。
编译器还生成一个func(m *Baz) GetFoo() *Bar
辅助函数。这使得可以在没有中间nil
检查的情况下链接获取调用。
Repeated
字段每个repeated
字段生成一个T
字段的切片(slice
)在Go结构中,其中T
是字段的元素类型。如下消息带有repeated
字段:
message Baz { repeated Bar foo = 1; }
编译器生成Go结构:
type Baz struct { Foo []*Bar }
同样:
repeated bytes foo = 1
;编译器将生成一个带有名为Foo
的[][]bytes
字段的Go结构repeated MyEnum bar = 2
;编译器将生成一个带有名为Bar
的[]MyEnum
字段的Go结构Map
字段每个map
字段以类型map[TKey]TValue
在结构体中生成一个字段,其中TKey
是字段的键类型,TValue
是字段的值类型。如下消息带有map
字段:
message Bar {} message Baz { map<string, Bar> foo = 1; }
编译器生成Go结构:
type Baz struct { Foo map[string]*Bar }
Oneof
字段对于oneof
字段,protobuf
编译器生成具有接口类型isMessageName_MyField
的单独字段。并且还为oneof
中的每个单独字段生成一个结构体。这些都实现了这个isMessageName_MyField
接口。
对于带有oneof字段的此消息:
package account; message Profile { oneof avatar { string image_url = 1; bytes image_data = 2; } }
编译器生成的结构体:
type Profile struct { // Types that are valid to be assigned to Avatar: // *Profile_ImageUrl // *Profile_ImageData Avatar isProfile_Avatar `protobuf_oneof:"avatar"` } type Profile_ImageUrl struct { ImageUrl string } type Profile_ImageData struct { ImageData []byte }
*Profile_ImageUrl
和*Profile_ImageData
都通过提供空的isProfile_Avatar()
方法来实现isProfile_Avatar
。
以下示例显示如何设置字段:
p1 := &account.Profile{ Avatar: &account.Profile_ImageUrl{"http://example.com/image.png"}, } // imageData is []byte imageData := getImageData() p2 := &account.Profile{ Avatar: &account.Profile_ImageData{imageData}, }
要访问该字段,可以使用值上的类型开关来处理不同的消息类型。
switch x := m.Avatar.(type) { case *account.Profile_ImageUrl: // Load profile image based on URL // using x.ImageUrl case *account.Profile_ImageData: // Load profile image based on bytes // using x.ImageData case nil: // The field is not set. default: return fmt.Errorf("Profile.Avatar has unexpected type %T", x) }
编译器还生成get方法func (m *Profile) GetImageUrl() string
和func (m *Profile) GetImageData() []byte
。每个get函数返回该字段的值,如果未设置则返回零值。
给出如下枚举:
message SearchRequest { enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } Corpus corpus = 1; ... }
protocol buffers
编译器生成一个类型和一系列该类型的常量。
对于上面消息中的枚举,类型名称以消息名称开头:
type SearchRequest_Corpus int32
对于包级别的枚举:
enum Foo { DEFAULT_BAR = 0; BAR_BELLS = 1; BAR_B_CUE = 2; }
Go生成的类型名称从未修改的proto
枚举名称得来:
type Foo int32
此类型具有String()
方法,该方法返回给定值的名称。
Enum()
方法使用给定值初始化新分配的内存并返回相应的指针:
func (Foo) Enum() *Foo
protocol buffers
编译器为枚举中的每个值生成一个常量。对于消息中的枚举,常量以消息的名称开头:
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 )
对于包级别的枚举,常量以枚举名称开头:
const ( Foo_DEFAULT_BAR Foo = 0 Foo_BAR_BELLS Foo = 1 Foo_BAR_B_CUE Foo = 2 )
protobuf
编译器还生成从整数值到字符串名称的映射以及从名称到值的映射:
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
文件中。
扩展仅存在于proto2中。有关proto2扩展的Go生成代码API的文档,请参阅proto包doc。
默认情况下,Go代码生成器不会为服务生成输出。如果启用gRPC插件(请参阅gRPC Go快速入门指南),则会生成代码以支持gRPC。