protobuf 2
定义一个消息
## protobuf 2
syntax = "proto2"; // 该文件的第一行指定您正在使用 proto2 语法。 这应该是文件的第一个非空、非注释行。
//required:一个格式良好的消息一定要含有1个这种字段。表示该值是必须要设置的;
//optional:消息格式中该字段可以有0个或1个值(不超过1个)。
//repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。相当于java中的List。
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
repeated int32 result_per_page = 3;
}
编号:
1 ~ 536870911(除去 19000 到 19999 之间的标识号, Protobuf 协议实现中对这些进行了预留。如果非要在.proto 文件中使用这些预留标识号,编译时就会报警)
在消息定义中,每个字段都有唯一的一个标识符。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改 变。注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该尽可能为那些频繁出现的消息元素保留 [1,15]之内的标识号。
protobuf类型 | java类型 | 默认值 |
---|---|---|
double | double | 0 |
float | float | 0 |
int32 | int | 0 |
int64 | long | 0 |
bool | boolean | false |
string | String | “” |
对于字符串,默认值为空字符串。 对于字节,默认值为空字节字符串。 对于布尔值,默认值为 false。 对于数字类型,默认值为零。 对于枚举,默认值是枚举类型定义中列出的第一个值。 这意味着在向枚举值列表的开头添加值时必须小心。可以通过default关键字改变默认值。
syntax = "proto2";
// package定义了该.proto文件的命名空间, 定义该属性不会对java类的输出目录产生影响,
// 但是会影响java类能否生成:定义了package后,可以生成两个属性完全一样的java文件(即两个proto文件只有message的名称和java_outer_classname不一样,其他完全一样)
// package只是定义命名空间,可以n个proto文件都定义相同的package,也可以不同(如果想要两个proto仅仅是java_outer_classname不一样,其他完全一样,就必须定义不同的命名空间)。如果想在两个proto文件中定义相同的enum,package必须不同(若不指明不同的package,即使两个proto文件仅仅是enum里的属性相同,其他完全不同,也是不能生成java文件的)
package org.example;
option java_package = "org.example.me"; // 输出java类所在目录
option java_outer_classname = "EnumTestMulti"; // 输出java类的名称为EnumTestMulti.java
// 是否输出多个java类,设置为true将根据message输出多个java类文件,设置为false一个proto文件只会输出一个java文件
option java_multiple_files = false;
message TestMulti {
optional string a = 1 [default = "aaa"];
optional int32 b = 2;
optional int64 c = 3;
optional bool d = 4;
optional float e = 5;
optional double f = 6;
repeated int64 g = 7;
required typeA2C t = 8 [default = B];
// key_type 可以是任何整数或字符串类型(即除了浮点数和bytes类型)。请注意,枚举不是有效的 key_type。value_type 可以是除 map 之外的任何类型
// maps 不能是 repeated、optional、required
map<string, int32> h = 9;
}
enum typeA2C {
A = 0;
B = 1;
C = 2;
}
消息嵌套和引用
syntax = "proto2";
package org.example;
import "importTest.proto";
option java_package = "org.example";
option java_outer_classname = "NestedMulti";
option java_multiple_files = false;
message Foo {
optional int32 val = 1;
optional immTest tt = 2;
}
message Bar {
optional Foo foo = 1;
}
message Baz {
optional Bar bar = 1;
}
syntax = "proto2";
package org.example;
option java_package = "org.example";
option java_outer_classname = "ImmTestDemo";
option java_multiple_files = false;
message immTest {
optional int32 abc = 1;
}
生成java类命令
# 不存在proto文件的相互import时可以这样生成 exe + 要生成java的proto文件路径 + java文件路径
./protoc-3.12.0-windows-x86_64.exe ./HelloWorld.proto --java_out=./
# 存在互相import时,必须用--proto_path指明要搜索import的proto文件路径 exe + import的proto文件路径 + java文件路径 + 要生成java的proto文件路径
./proto/protoc-3.12.0-windows-x86_64.exe --proto_path=./proto --java_out=./java ./proto/*.proto
代码示例
package org.example;
import com.google.protobuf.util.JsonFormat;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
public class App {
public static void main( String[] args ) {
EnumTestMulti.TestMulti.Builder TestMultibuilder = EnumTestMulti.TestMulti.newBuilder();
EnumTestMulti.TestMulti message = TestMultibuilder.setA("a").setB(2).setC(3L).setD(true).setE(0.1f).setF(4D).addG(5L)
.setT(EnumTestMulti.typeA2C.B).putH("HKey", 6).build();
// 序列化
byte[] bytes = message.toByteArray();
// 反序列化
try {
/**
* parseFrom: 可以从ByteBuffer, InputStream, byte[]等反序列化
*/
EnumTestMulti.TestMulti testMulti = EnumTestMulti.TestMulti.parseFrom(bytes);
System.out.println("=======================打印消息==============================");
//嵌套builder
System.out.println(testMulti);
} catch (Exception e) {
// TODO
}
// 将消息写到输出流
OutputStream outputStream = new ByteArrayOutputStream();
try {
message.writeTo(outputStream);
} catch (Exception e) {
// TODO
}
System.out.println("=======================嵌套builder==============================");
//嵌套builder
NestedMulti.Foo foo = NestedMulti.Baz.newBuilder().getBarBuilder().getFooBuilder().setVal(100)
// 设置import消息的值
.setTt(ImmTestDemo.immTest.newBuilder().setAbc(888))
.build();
System.out.println(foo);
System.out.println("=========================mergeFrom============================");
EnumTestMulti.TestMulti.Builder emptybuilder = EnumTestMulti.TestMulti.newBuilder();
// mergeFrom 相当于BeanUtils.copyProperties(dest, orig)
EnumTestMulti.TestMulti.Builder newBuilder = emptybuilder.mergeFrom(message);
System.out.println(newBuilder.build());
System.out.println("======================protobuf转json================================");
// protobuf转json
String json = null;
try {
json = JsonFormat.printer().print(foo);
System.out.println(json);
} catch (Exception e) {
// TODO
}
System.out.println("========================json转protobuf============================");
// json转protobuf
NestedMulti.Foo.Builder fooBuilder = NestedMulti.Baz.newBuilder().getBarBuilder().getFooBuilder();
NestedMulti.Foo msg = null;
try {
JsonFormat.parser().merge(json, fooBuilder);
msg = fooBuilder.build();
System.out.println(msg);
} catch (Exception e) {
// TODO
}
System.out.println("========================复制嵌套消息=============================");
// mergeFrom可以复制嵌套消息
NestedMulti.Foo.Builder fooBuilderDeep = NestedMulti.Baz.newBuilder().getBarBuilder().getFooBuilder();
fooBuilderDeep.mergeFrom(msg);
System.out.println(fooBuilderDeep.build());
}
}