定义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