zoukankan      html  css  js  c++  java
  • Protobuf动态解析在Java中的应用 包含例子程序

    最近在做ProtoBuf相关的项目,其中用到了动态解析,网上看了下相关资料和博文都比较少,自己来写一个记录一下学习过程。
     
    Protocol Buffers是结构化数据格式标准,提供序列化和反序列方法,用于存储和交换。语言中立,平台无关、可扩展。目前官方提供了C++、Java、Python API,也有其他语言的开源api(比如php)。可通过 .proto文件生成对应语言的类代码
    如果已知protobuf内容对应的是哪个类对象,则可以直接使用反序列化方法搞定(Xxx.parseFrom(inputStream)由二进制转换,TextFormat.merge(string, xxxBuilder)由文本转换)
     
    而我们经常遇到的情况是,拿到一个被protobuf序列化的二进制内容,但不知道它的类型,无法获得对应的类对象。这种多见于需要处理各种各样未知的ProtoBuf对象的系统。ProtoBuf提供了动态解析机制来解决这个问题,它要求提供二进制内容的基础上,再提供对应类的Descriptor对象,在解析时通过DynamicMessage类的成员方法来获得对象结果。
    最后问题就是Descriptor对象从哪里来?这是通过protoc --descriptor_set_out=$outputpath 命令生成descriptor文件,进而得到的。代码如下:
     1 option java_package="com.liulei.cinema";
     2 
     3 enum MovieType{
     4     CHILDREN=1;
     5     ADULT=2;
     6     NORMAL=3;
     7     OHTER=4;
     8 }
     9 
    10 enum Gender{
    11     MAN=1;
    12     WOMAN=2;
    13     OTHER=3;
    14 }
    15 
    16 message Movie{
    17     required string name=1;
    18     required MovieType type=2;
    19     optional int32 releaseTimeStamp=3;
    20     optional string description=4;
    21 }
    22 
    23 message Customer{
    24     required string name=1;
    25     optional Gender gender=2;
    26     optional int32 birthdayTimeStamp=3;
    27 }
    28 
    29 message Ticket{
    30     required int32 id=1;
    31     required Movie movie=2;
    32     required Customer customer=3;
    33 }
    cinema.proto
     1 public static void main( String[] args ) {
     2 
     3         Cinema.Movie.Builder movieBuilder = Cinema.Movie.newBuilder();
     4         movieBuilder.setName("The Shining");
     5         movieBuilder.setType(Cinema.MovieType.ADULT);
     6         movieBuilder.setReleaseTimeStamp(327859200);
     7 
     8         System.out.println("Dynamic Message Parse by proto file");
     9         try {
    10             byte[] buffer3 = new byte[movieBuilder.build().getSerializedSize()];
    11             CodedOutputStream codedOutputStream3 = CodedOutputStream.newInstance(buffer3);
    12             try {
    13                 movieBuilder.build().writeTo(codedOutputStream3);
    14                 System.out.println(buffer3);
    15             } catch (IOException e) {
    16                 e.printStackTrace();
    17             }
    18             String protocCMD = "protoc --descriptor_set_out=cinema.description ./cinema.proto --proto_path=.";
    19             Process process = Runtime.getRuntime().exec(protocCMD);
    20             process.waitFor();
    21             int exitValue = process.exitValue();
    22             if (exitValue != 0) {
    23                 System.out.println("protoc execute failed");
    24                 return;
    25             }
    26             Descriptors.Descriptor pbDescritpor = null;
    27             DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(new FileInputStream("./cinema.description"));
    28             for (DescriptorProtos.FileDescriptorProto fdp : descriptorSet.getFileList()) {
    29                 Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fdp, new Descriptors.FileDescriptor[]{});
    30                 for (Descriptors.Descriptor descriptor : fileDescriptor.getMessageTypes()) {
    31                     if (descriptor.getName().equals("Movie")) {
    32                         System.out.println("Movie descriptor found");
    33                         pbDescritpor = descriptor;
    34                         break;
    35                     }
    36                 }
    37             }
    38             if (pbDescritpor == null) {
    39                 System.out.println("No matched descriptor");
    40                 return;
    41             }
    42             DynamicMessage.Builder pbBuilder = DynamicMessage.newBuilder(pbDescritpor);
    43 
    44             Message pbMessage = pbBuilder.mergeFrom(buffer3).build();
    45             System.out.println(pbMessage);
    46 
    47         } catch (Exception e) {
    48             System.out.println("Exception");
    49             e.printStackTrace();
    50         }
    51     }
    Main.java
    执行结果:

    Dynamic Message Parse From byte array
    [B@597ccf6e
    Movie descriptor found
    name: "The Shining"
    type: ADULT
    releaseTimeStamp: 327859200

     
    解释具体过程:
    0.首先对.proto文件使用protoc命令,生成的descriptor文件中包含多个类对应的descriptor类信息(序列化的DescriptorSet内容)
    1.首先取出序列化的DescriptorSet内容,FileDescriptorSet.parseFrom方法反序列化得到FileDescriptorSet对象
    2.取出对应message类型的Descriptor。
         DescriptorSet成员方法getFileList(),拿到多个FileDescriptorProto对象,再构建对应FileDescriptor。
         FileDescriptor的成员方法getMessageTypes()得到所有Message的Descriptor对象,找到对应名字的Descriptor
    3.用Descriptor对象反序列化对象
         构建DynamicMessage.Builder对象builder,再调用builder的mergeFrom/merge方法得到Message对象
     
    其中Descriptor相关类:

    DescriptorProtos.FileDescriptorSet:protoc编译出来类文件中包含这个类,描述多个.proto文件中的类

    DescriptorProtos.FileDescriptorProto:描述一个完整的.proto文件中的类
    DescriptorProtos.FileDescriptor:由DescriptorProtos.FileDescriptorProto构建而来(buildFrom),描述1个完整.proto文件中的所有内容,包括message类型的Descriptor和其他被导入文件的Descriptor。
          getMessageTypes()方法:返回List<Descriptors.Descriptor>。得到FileDescriptor内,所有message类型直接儿子的Descriptor列表    
    DescriptorProtos.Descriptor:描述一个message类型,通过getName()得到message的类名
     
     转载请注明出处。欢迎拍砖~
     
     
     
  • 相关阅读:
    为什么 TCP 建立连接是三次握手,关闭连接确是四次挥手呢?
    一文搞懂 Java 中的枚举,写得非常好!
    IntelliJ IDEA For Mac 快捷键,够骚,速度收藏!
    Java Bean 为什么必须要有一个无参构造函数?
    18 个示例带你掌握 Java 8 日期时间处理!
    从入门到熟悉 HTTPS 的 9 个问题
    MyBatis的动态SQL详解
    MyBatis总结-实现关联表查询
    AngularJS
    Spring MVC url提交参数和获取参数
  • 原文地址:https://www.cnblogs.com/liulaoshi/p/7226439.html
Copyright © 2011-2022 走看看