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