将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的定义让客户端知道,而是将定义随着消息一起打包,对于需要极度灵活结构的需求可以使用。