proto文件
protobuf的定義是通過proto文件進行定義的,一個標准的類型如下:
message SearchRequest {
required string query = 1;
optional int32 page_number = 2 [default = 10];
optional int32 result_per_page = 3;
}
其中message定義了類型名字,其中每一個字段有三個選項:
- required:字段必填。
- optional: 字段選填,不填就會使用默認值,默認數值類型的默認值為0,string類型為空字符串,枚舉類型為第一個枚舉值。
- repeated: 數組類型,可以放入多個類型實例。
之后需要跟上數據類型,在類型之后為字段名。最后跟上“=N”這里N是標記位,每個字段都有標記位,各個字段不能重復且必須為正值,其最大值為 2^29 - 1,同時protobuf內部預留了19000到19999不能被用戶使用,官方建議將常用的字段放在前面,由於這個字段的大小隨着數值大小增加,如1-16只占用一個字節。最后可以跟上自定義的默認值。
在一個proto文件中可以存放多個message,message內部也可以定義message,外部如需調用需要指明對應的層級關系。同時可以使用import引入外部的proto文件:
//引入外部proto文件
import "other.proto";
//引入外部proto文件,並讓引入了該文件的proto文件也能訪問被引入類型。
import public "other.proto";
還可以在proto文件中各個級別增加部分編譯設置,常用包括:
- java_package:生成的java包名
- java_outer_classname :生成的java類名
- optimize_for:設置編譯優化級別,SPEED-默認值速度優先,CODE_SIZE-最小代碼量,LITE_RUNTIME-最小運行時占用(適用於環境受限的情況)
數據類型
基礎數據類型
protobuf支持大多數基礎數據類型,下表包含常用類型,詳細列表見官方文檔
.proto | java實現 | desc |
---|---|---|
double | double | |
float | float | |
int32 | int | 有符號整形建議使用sint32 |
uint32 | int | 無符號整形 |
sint32 | int | 有符號整形 |
int64 | long | 有符號長整形建議使用sint64 |
uint64 | long | 無符號長整形 |
sint64 | long | 有符號長整形 |
bool | boolean | |
string | String | |
byte | ByteString | |
message CompositeType {
optional MessageType message = 1;
}
###oneof
oneof是一種特殊類型可以綁定一組變量,但是只有最后設置的那個變量才生效,之前的變量都會被清除:
-------proto------
message Foo {
oneof test_oneof {
string name = 1;
int32 id = 2;
}
}
-------java-------
System.out.println(Demo.Foo.newBuilder().setId(1).setName("name").build().toString());
System.out.println(">>>");
System.out.println(Demo.Foo.newBuilder().setName("name").setId(1).build().toString());
-------輸出-------
name: "name"
id: 1
###map
map類型可以接受鍵值對,鍵可以使用string或數值類型,值可以使用任意類型:
-------proto------
message Foo {
map<string, string> bar = 1;
}
-------java-------
Demo.Foo foo=Demo.Foo.newBuilder().putBar("key1","value1").putBar("key2","value2").build();
FileOutputStream fos=new FileOutputStream("D://person");
foo.writeTo(fos);
fos.close();
FileInputStream fis=new FileInputStream("D://person");
Demo.Foo foo2=Demo.Foo.parseFrom(fis);
System.out.println(foo2.getBarCount());
fis.close();
##extension
Extension有點類似繼承,可以向message對象內增加額外的字段:
message Foo {
// ...
extensions 100 to 199; //首先需要定義100-199為extension字段
}
extend Foo {
optional int32 bar = 100; //增加bar字段
}
在使用extension時和普通字段有些不同,Java中如下:
public static void main(String[] args) throws IOException, ClassNotFoundException {
//通過setExtension設置字段值
Demo.Foo foo=Demo.Foo.newBuilder().setExtension(Demo.bar,1).build();
//通過getExtension可以取值
System.out.println(foo.getExtension(Demo.bar));
FileOutputStream fos=new FileOutputStream("D://person");
foo.writeTo(fos);
fos.close();
FileInputStream fis=new FileInputStream("D://person");
//反序列化時需要注冊對應的extension字段,不然無法取到extesion的值
ExtensionRegistry registry = ExtensionRegistry.newInstance();
registry.add(Demo.bar);
Demo.Foo foo2=Demo.Foo.parseFrom(fis,registry);
System.out.println(foo2.getExtension(Demo.bar));
fis.close();
}