文档格式
syntax = "proto2";
package godme;
option java_package = "com.godme.protobuf";
option java_outer_classname = "Message";
message Person{
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType{
MOBILE = 1;
HOME = 2;
WORK = 3;
}
message PhoneNumber{
required string number = 1;
optional PhoneType type = 2 [ default = HOME ];
}
repeated PhoneNumber phones = 4;
}
message AddressBook{
repeated Person person = 1;
}
版本( syntax)
syntax = "proto2";
指定protoc编译版本。
名称空间(package)
package godme;
指定名称空间(namespace),为区别同名文件。
默认情况下,作为编译的java文件的包名。
参数
option java_package = "com.godme.protobuf";
option java_outer_classname = "Message";
java_package:指定编译的java文件的包名,不指定默认采用package指定的名称。
java_outer_classname:指定编译的java文件的类名,默认为Message。
类型
| type | description |
|---|---|
string |
字符串 |
int32 |
int |
int64 |
long |
enum |
枚举 |
other |
其他复杂类型必须在proto文件内定义才能使用可以通过 import关键字导入其他proto文件定义的数据类型 |
修饰符
| type | description |
|---|---|
required |
必须,如果标记为required当序列化或反序列化时该参数没有设置具体值,会抛出异常 |
optinoal |
可选,序列化和反序列化时刻不必设置具体值 会根据类型自动设置默认值,也可以自己指定默认值 默认值下一个表进行说明 |
repeated |
重复,单一类型的多个实例 相当于 List,是指定对象的集合 |
默认值
| type | default |
|---|---|
string |
"",空字符串 |
int32 |
0 |
int64 |
0 |
boolean |
false |
optional PhoneType type = 2 [ default = HOME ];
通过[ default = value ]可指定默认值,记得修饰符要为optinoal。
标记符
required string name = 1;
required int32 id = 2;
optional string email = 3;
声明时=等号后面的,属于标记符,不是赋值操作。
使用15及一下的数字进行标记的字段,序列化是会使用更少的空间。
常用的关键属性尽量使用小数进行标记,以便于更快的序列或反序列化。
类管理
- 定义
以message关键字进行声明,然后用{}进行包括。
在内部依据前面的讲述,进行结构体的定义。
- 结构
所有的message都定义在单个文件内部,message可以进行嵌套。
生成的java文件,类定义都在同一个java文件当中,是java_outer_classname的内部类。
至于嵌套的message,那就是内部类的内部类。
编译方式
protoc --proto_path=. --java_out=./../java message.proto
-
protoc:编译命令 -
--proto_path:指定proto文件的目录 -
--java_out:指定编译的java文件的路径 -
message.proto:proto文件
常用的就是这两个选项,根据语言的不同,还有:
| option | language |
|---|---|
--cpp_out |
c++ |
--csharp_out |
c# |
--js_out |
javascript |
--objc_out |
Object-c |
--php_out |
PHP |
--python_out |
Python |
--ruby_out |
Ruby |
可以看到,
protobuf支持的语言还是挺多的。而且,添加多个
out,可以一次性编译出多个语言的Class文件哦。
使用办法
编译成的
java文件,会报错,需要protobuf-java这个jar包的支持。compile( 'io.netty:netty-all:4.1.10.Final', 'com.google.protobuf:protobuf-java:3.6.1', 'com.google.protobuf:protobuf-java-util:3.6.1' )
Builder
public class Main {
public static void main(String[] args) {
Message.Person.Builder builder = Message.Person.newBuilder();
if(!builder.hasId()){
builder.setId(1);
}
if(!builder.hasName()){
builder.setName("godme");
}
if(!builder.hasEmail()){
builder.setEmail("[email protected]");
}
Message.Person godme = builder.build();
}
}
proto采用构造器模式,每次创建对象,都只能build出来。
builder只有两个方法
hasAttr:判断属性是否被设置(默认值)setAttr:设置对应属性如果是已有对象的其他模式,可以通过
parseFrom进行反序列化,支持
StringBufferbyteArray更多类型参看
API
Object
Message.Person person = builder.build();
Integer id = person.getId();
String name = person.getName();
String email = person.getEmail();
person.toBuilder();
person.toByteArray();
person.toByteString();
对象的方法,就是
getAttr和toSomething
getAttr:获取属性toSomething:序列化操作
简单操作
code
public class Main {
public static void main(String[] args) throws IOException {
Message.Person person = Message.Person.newBuilder()
.setId(1)
.setName("godme")
.setEmail("[email protected]")
.build();
System.out.println(person.toString());
String fileName = "person";
writeToFile(person, fileName);
Message.Person person2 = readFromFile(fileName);
System.out.println(person2);
System.out.println(person.equals(person2));
}
public static void writeToFile(Message.Person person, String fileName) throws IOException {
FileOutputStream outputStream = new FileOutputStream(fileName);
person.writeTo(outputStream);
outputStream.flush();
outputStream.close();
}
public static Message.Person readFromFile(String fileName) throws IOException {
FileInputStream fileInputStream = new FileInputStream(fileName);
Message.Person person = Message.Person.parseFrom(fileInputStream);
return person;
}
}
-
result
小结
嗯,就是这样。
机器之间呢,就是通过流传输,就是要序列化/反序列化。
用这种东西,你可以把一个对象就这样传输到另一个机器上哦。
当然,必须是socket的流了,其他的也行。
代码在此