這個教程中將會描述protocol buffer編譯器經過給定的.proto
會編譯生成什麼Go代碼。教程針對的是proto3版本的protobuf。在閱讀以前確保你已經閱讀過Protobuf語言指南。git
Protobuf核心的工具集是C++語言開發的,官方的protoc編譯器中並不支持Go語言,須要安裝一個插件才能生成Go代碼。用以下命令安裝:github
$ go get github.com/golang/protobuf/protoc-gen-go
複製代碼
提供了一個protoc-gen-go
二進制文件,當編譯器調用時傳遞了--go_out
命令行標誌時protoc
就會使用它。--go_out
告訴編譯器把Go源代碼寫到哪裏。編譯器會爲每一個.proto
文件生成一個單獨的源代碼文件。golang
輸出文件的名稱是經過獲取.proto文件的名稱並進行兩處更改來計算的:bash
.pb.go
。好比說player_record.proto
編譯後會獲得player_record.pb.go
。--proto_path
或-I
命令行標誌指定)將替換爲輸出路徑(使用--go_out
標誌指定)。當你運行以下編譯命令時:markdown
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
文件中有包聲明,生成的源代碼將會使用它來做爲Go的包名,若是.proto
的包名中有.
在Go包名中會將.
轉換爲_
。舉例來講proto
包名example.high_score
將會生成Go包名example_high_score
。oop
在.proto
文件中可使用option go_package
指令來覆蓋上面默認生成Go包名的規則。好比說包含以下指令的一個.proto
文件post
package example.high_score; option go_package = "hs"; 複製代碼
生成的Go源代碼的包名是hs
。ui
若是一個.proto
文件中不包含package聲明,生成的源代碼將會使用.proto
文件的文件名(去掉擴展名)做爲Go包名,.
會被首先轉換爲_
。舉例來講一個名爲high.score.proto
不包含pack聲明的文件將會生成文件high.score.pb.go
,他的Go包名是high_score
。
一個簡單的消息聲明:
message Foo {}
複製代碼
protocol buffer編譯器將會生成一個名爲Foo
的結構體,實現了proto.Message
接口的Foo
類型的指針
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的內部。好比說:
message Foo {
message Bar {
}
}
複製代碼
這種狀況,編譯器會生成兩個結構體:Foo
和Foo_Bar
。
Protobufs帶有一組預約義的消息,稱爲衆所周知的類型(WKT)。這些類型能夠用於與其餘服務的互操做性,或者僅僅由於它們簡潔地表示了常見的有用模式。例如,Struct消息表示任意C樣式結構的格式。
WKT的預生成Go代碼做爲Go protobuf庫的一部分進行分發,若是message中使用了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]包,並正常使用該類型。
編譯器會爲每一個在message中定義的字段生成一個Go結構體的字段,字段的確切性質取決於它的類型以及它是singular
,repeated
,map
仍是oneof
字段。
注意生成的Go結構體的字段將始終使用駝峯命名,即便在.proto
文件中消息字段用的是小寫加下劃線(應該這樣)。大小寫轉換的原理以下:
_
,它將被替換爲X。所以,proto字段foo_bar_baz
在Go中變成FooBarBaz
, _my_field_name_2
變爲XMyFieldName_2
。
對於字段定義:
int32 foo = 1;
複製代碼
編譯器將生成一個帶有名爲Foo的int32字段和一個訪問器方法GetFoo()的結構,該方法返回Foo中的int32值或該字段的零值(若是字段未設置(數值型零值爲0,字符串爲空字符串))。
給出以下消息類型
message Bar {}
複製代碼
對於一個有Bar
類型字段的消息:
// proto3
message Baz {
Bar foo = 1;
}
複製代碼
編譯器將會生成一個Go結構體
type Baz struct { Foo *Bar } 複製代碼
消息類型的字段能夠設置爲nil,這意味着該字段未設置,有效清除該字段。這不等同於將值設置爲消息結構體的「空」實例。
編譯器還生成一個func(m * Baz)GetFoo()* Bar
輔助函數。這讓不在中間檢查nil值進行鏈式調用成爲可能。
每一個重複的字段在Go中的結構中生成一個T類型的slice,其中T是字段的元素類型。對於帶有重複字段的此消息:
message Baz {
repeated Bar foo = 1;
}
複製代碼
編譯器會生成以下結構體:
type Baz struct { Foo []*Bar } 複製代碼
一樣,對於字段定義repeated bytes foo = 1;
編譯器將會生成一個帶有類型爲[][]byte
名爲Foo
的字段的Go結構體。對於可重複的枚舉repeated MyEnum bar = 2;
,編譯器會生成帶有類型爲[]MyEnum
名爲Bar
的字段的Go結構體。
每一個映射字段會在Go的結構體中生成一個map[TKey]TValue
類型的字段,其中TKey
是字段的鍵類型TValue
是字段的值類型。對於下面這個消息定義:
message Bar {}
message Baz {
map<string, Bar> foo = 1;
}
複製代碼
編譯器生成Go結構體
type Baz struct { Foo map[string]*Bar } 複製代碼
給出以下枚舉
message SearchRequest {
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 1;
...
}
複製代碼
編譯器將會生成一個枚舉類型和一系列該類型的常量。
對於消息中的枚舉(像上面那樣),類型名字以消息名開頭
type SearchRequest_Corpus int32 複製代碼
對於包級別的枚舉:
// .proto
enum Foo {
DEFAULT_BAR = 0;
BAR_BELLS = 1;
BAR_B_CUE = 2;
}
複製代碼
Go 中的類型不會對proto中的枚舉名稱進行修改:
type Foo int32 複製代碼
此類型具備String()
方法,該方法返回給定值的名稱。
Enum()
方法使用給定值初始化新分配的內存並返回相應的指針:
func (Foo) Enum() *Foo
複製代碼
編譯器爲枚舉中的每一個值生成一個常量。對於消息中的枚舉,常量以消息的名稱開頭:
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
文件中首先出現的名稱。
默認狀況下,Go代碼生成器不會爲服務生成輸出。若是您啓用gRPC插件(請參閱gRPC Go快速入門指南),則會生成代碼以支持gRPC。