gpt4 book ai didi

基于Protobuf动态解析在Java中的应用 包含例子程序

转载 作者:qq735679552 更新时间:2022-09-28 22:32:09 26 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章基于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文件,进而得到的.

代码如下:

 cinema.proto 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
option java_package= "com.liulei.cinema" ;
 
enum MovieType{
  CHILDREN= 1 ;
  ADULT= 2 ;
  NORMAL= 3 ;
  OHTER= 4 ;
}
 
enum Gender{
  MAN= 1 ;
  WOMAN= 2 ;
  OTHER= 3 ;
}
 
message Movie{
  required string name= 1 ;
  required MovieType type= 2 ;
  optional int32 releaseTimeStamp= 3 ;
  optional string description= 4 ;
}
 
message Customer{
  required string name= 1 ;
  optional Gender gender= 2 ;
  optional int32 birthdayTimeStamp= 3 ;
}
 
message Ticket{
  required int32 id= 1 ;
  required Movie movie= 2 ;
  required Customer customer= 3 ;
}

Main.java 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public static void main( String[] args ) {
 
   Cinema.Movie.Builder movieBuilder = Cinema.Movie.newBuilder();
   movieBuilder.setName( "The Shining" );
   movieBuilder.setType(Cinema.MovieType.ADULT);
   movieBuilder.setReleaseTimeStamp( 327859200 );
 
   System.out.println( "Dynamic Message Parse by proto file" );
   try {
    byte [] buffer3 = new byte [movieBuilder.build().getSerializedSize()];
    CodedOutputStream codedOutputStream3 = CodedOutputStream.newInstance(buffer3);
    try {
     movieBuilder.build().writeTo(codedOutputStream3);
     System.out.println(buffer3);
    } catch (IOException e) {
     e.printStackTrace();
    }
    String protocCMD = "protoc --descriptor_set_out=cinema.description ./cinema.proto --proto_path=." ;
    Process process = Runtime.getRuntime().exec(protocCMD);
    process.waitFor();
    int exitValue = process.exitValue();
    if (exitValue != 0 ) {
     System.out.println( "protoc execute failed" );
     return ;
    }
    Descriptors.Descriptor pbDescritpor = null ;
    DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom( new FileInputStream( "./cinema.description" ));
    for (DescriptorProtos.FileDescriptorProto fdp : descriptorSet.getFileList()) {
     Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fdp, new Descriptors.FileDescriptor[]{});
     for (Descriptors.Descriptor descriptor : fileDescriptor.getMessageTypes()) {
      if (descriptor.getName().equals( "Movie" )) {
       System.out.println( "Movie descriptor found" );
       pbDescritpor = descriptor;
       break ;
      }
     }
    }
    if (pbDescritpor == null ) {
     System.out.println( "No matched descriptor" );
     return ;
    }
    DynamicMessage.Builder pbBuilder = DynamicMessage.newBuilder(pbDescritpor);
 
    Message pbMessage = pbBuilder.mergeFrom(buffer3).build();
    System.out.println(pbMessage);
 
   } catch (Exception e) {
    System.out.println( "Exception" );
    e.printStackTrace();
   }
  }

执行结果:

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.DescriptorSet: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的类名 。

以上这篇基于Protobuf动态解析在Java中的应用 包含例子程序就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我.

最后此篇关于基于Protobuf动态解析在Java中的应用 包含例子程序的文章就讲到这里了,如果你想了解更多关于基于Protobuf动态解析在Java中的应用 包含例子程序的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com