定義Proto的文件應以.proto爲後綴。html
Proto文件的首行應指定語法版本:java
syntax = "proto3"; // "proto2"
在消息中,每一個字段如下列方式定義:git
type filed "=" tag ";"
如:github
message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; }
出於性能考慮,在消息中,經常使用字段的標籤最好小於15。這樣能夠下降消息序列化後的體積。golang
一個Proto文件中,能夠定義多個消息。如:app
message SearchRequest { // ... } message SearchResponse { // ... }
Proto使用C風格的註釋。maven
對於C++,protobuf爲每一個消息生成一個類,爲每一個proto文件生成一個.h頭文件和一個.cc源代碼文件。性能
對Java,protobuf爲每一個消息生成一個類和一個Builder類。gradle
對於Go,protobuf爲每一個消息生成一個.pb.go源代碼文件和一個結構體。ui
對Objective-C,protobuf爲每一個proto文件生成一個pbobjc.h頭文件和一個pbobjc.m文件,爲每一個消息生成一個類。
bool string bytes int32 int64 uint32 uint64 double float
類型 | 默認值 |
bool | false |
bytes | [] |
numeric | 0 |
enum | FirstElement |
Field | null |
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; } message EnumAllowingAlias { option allow_alias = true; UNKNOWN = 0; STARTED = 1; RUNNING = 1; }
message SearchResponse { repeated Result results = 1; } message Result { string url = 1; string title = 2; repeated string snippets = 3; }
import "myproject/other_protos.proto";
-I/–proto_path 指定proto文件目錄。
message SearchResponse { message Result { string url = 1; string title = 2; repeated string snippets = 3; } repeated Result results = 1; } message SomeOtherMessage { SearchResponse.Result result = 1; }
不得改動已存在的字段標籤。新生成的代碼能夠解析舊消息。新增的字段會被設置爲默認值。舊代碼也能夠解析新消息,新增的字段會被忽略。
字段能夠被刪除。但已使用過的標籤不得重複使用。
當字符串是UTF-8編碼時,bytes和string能夠兼容。
Any消息是一個佔位符,表示任意類型。使用Any消息時,須要引用google/protobuf/any.proto。
import "google/protobuf/any.proto"; message ErrorStatus { string message = 1; repeated google.protobuf.Any details = 2; } NetworkErrorDetails details = ...; ErrorStatus status; status.add_details()->PackFrom(details); ErrorStatus status = ...; for (const Any& detail : status.details()){ if (detail.Is<NetworkErrorDetails>()){ NetworkErrorDetails network_error; detail.UnpackTo(&network_error); ... } }
OneOf提供了一種相似C語言union結構體的機制,來下降存儲體積。
message SampleMessage { oneof test_oneof { string name = 4; SubMessage sub_message = 9; } } SampleMessage message; message.set_name("Joe"); assert(message.has_name());
Map能夠定義一組鍵值對。
map<string, Project> projects = 3;
package foo.bar;
service SearchService { rpc Search (SearchRequest) returns (SearchResponse); }
package tutorial; option java_package = "com.example.tutorial"; option java_outer_classname = "AddressBookProtos"; message Person { required string name = 1; required int32 id = 2; string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; }
import ( "github.com/golang/protobuf/proto" pb "path/to/generated/pb/file" ) // ... p := &pb.Person { Id: 1234, Name: "John Doe", Email: "jdoe@example.com", Phones: []*pb.Person_PhoneNumber { {Number: "555-4321", Type: pb.Person_HOME}, }, } out, err := proto.Marshal(p) q := &pb.Person{} err := proto.Unmarshal(in, q) proto.MessageType(name string) reflect.Type proto.Clone(pb Message) Message
Any消息能夠表示任意類型的消息。在Go中使用Any消息的示例以下:
import "github.com/golang/protobuf/ptypes" import "github.com/golang/protobuf/proto" import "path/to/generated/pb" // message Foo { // google.protobuf.Any bar = 1; // } // message Bar { // uint32 x = 1; // } bar := &pb.Bar{ X: 1, } body, err := ptypes.MarshalAny(bar) if err != nil { log.Fatal(err) } foo := &pb.Foo{ Bar: body, }
在使用proto.Unmarshal(buf, message)對消息進行反序列化時,緩衝區buf的長度應當等於消息的實際長度。不然會報告以下錯誤消息:
proto: protocol.Message: illegal tag 0 (wire type 0)
package tutorial; option java_package = "com.example.tutorial"; option java_outer_classname = "AddressBookProtos"; message Person { required string name = 1; required int32 id = 2; string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; }
// Person public boolean hasName(); public String getName(); public boolean hasId(); public int getId(); public boolean hasEmail(); public String getEmail(); public List<PhoneNumber> getPhoneList(); public int getPhoneCount(); public PhoneNumber getPhone(int index); // Person.Builder public boolean hasName(); public java.lang.String getName(); public Builder setName(String value); public Builder clearName(); public List<PhoneNumber> getPhoneList(); public int getPhoneCount(); public PhoneNumber getPhone(int index); public Builder setPhone(int index, PhoneNumber value); public Builder addPhone(PhoneNumber value); public Builder addAllPhone(iterable<PhoneNumber> value); public Builder clearPhone();
Person john = Person.newBuilder() .setId(1234) .setName("John") .addPhone( Person.PhoneNumber.newBuilder() .setNumber("555-4321") .setType(Person.PhoneType.HOME)) .build(); john.writeTo(outputStream); Person walliam = Person.parseForm(inputStream);
google提供了生成protobuf的gradle插件,名稱是com.google.protobuf。在使用時,須要在build.gradle中加入:
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1' } } apply plugin: 'java' apply plugin: 'com.google.protobuf' protobuf { generatedFilesBaseDir = '$projectDir/src' protoc { // use local protoc // path = '/usr/local/bin/protoc' // or, get from repo artifact = 'com.google.protobuf:protoc:3.3.0' } plugins { grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.4.0' } } generateProtoTasks { all()*.plugins { grpc {} } } } repositories { mavenCentral() } dependencies { compile 'com.google.protobuf:protobuf-java:3.3.1' compile 'io.grpc:grpc-netty:1.4.0' compile 'io.grpc:grpc-protobuf:1.4.0' compile 'io.grpc:grpc-stub:1.4.0' /* for Android client, use compile 'io.grpc:grpc-okhttp:1.4.0' compile 'io.grpc:grpc-protobuf-lite:1.4.0' compile 'io.grpc:grpc-stub:1.4.0' */ } sourceSets { main { proto { srcDir 'src/main/protobuf' include '**/*.proto' } } }
而後執行:
gradle build
若是隻須要生成java源代碼文件,能夠執行:
gradle generateProto