將proto的定義和序列化的數據組成一個對象,在解碼時使用message內部存儲的proto定義和數據就可以實現proto消息的自解釋。
代碼
在proto發布的包內自帶了descriptor引入該類型組裝成如下格式:
syntax = "proto2";
import 'google/protobuf/descriptor.proto';
message SelfDescribingMessage {
// proto的定義
required google.protobuf.FileDescriptorProto proto_file = 1;
// 具體的message類型,由於一個proto中可以包含多個message
required string type_name = 2;
// 具體序列化的數據
required bytes message_data = 3;
}
然后可以隨便定義一個proto類型
syntax = "proto2";
message Person{
optional string name=1;
optional int32 age=2;
}
下面進行自定義類型的序列化和反序列化:
//序列化
//創建對象
byte[] data=Type.Person.newBuilder().setName("Myname").setAge(18).build().toByteArray();
//獲得proto定義
DescriptorProtos.FileDescriptorProto desc=Type.getDescriptor().toProto();
//組件自解釋對象,typename是具體message的名字
Demo.SelfDescribingMessage message=Demo.SelfDescribingMessage.newBuilder().setProtoFile(desc)
.setTypeName("Person").setMessageData(ByteString.copyFrom(data)).build();
//進行序列化
FileOutputStream fos=new FileOutputStream("D://message");
message.writeTo(fos);
fos.close();
//反序列化
//讀取文件
FileInputStream fis=new FileInputStream("D://message");
Demo.SelfDescribingMessage fileMessage=Demo.SelfDescribingMessage.parseFrom(fis);
//獲得proto文件
DescriptorProtos.FileDescriptorProto fdp=fileMessage.getProtoFile();
Descriptors.FileDescriptor[] fds={};
//根據typename獲得具體的message定義
Descriptors.Descriptor fileDesc=Descriptors.FileDescriptor.buildFrom(fdp,fds).findMessageTypeByName(fileMessage.getTypeName());
//讀出消息並打印
System.out.println(DynamicMessage.parseFrom(fileDesc,fileMessage.getMessageData().toByteArray()));
fis.close();
下面就是程序的輸出
name: "Myname"
age: 18
總結
通過如上方法可以不用事先將proto的定義讓客戶端知道,而是將定義隨着消息一起打包,對於需要極度靈活結構的需求可以使用。