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

 更新时间:2017年07月24日 08:32:41   投稿:jingxian  
下面小编就为大家带来一篇基于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

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

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中的应用 包含例子程序就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java游戏开发拼图游戏经典版

    Java游戏开发拼图游戏经典版

    这篇文章主要介绍了Java游戏开发拼图游戏经典版,对这方面感兴趣的同学可以跟着教程试下
    2021-01-01
  • Java死锁代码实例及产生死锁必备的四个条件

    Java死锁代码实例及产生死锁必备的四个条件

    这篇文章主要介绍了Java死锁代码实例及产生死锁必备的四个条件,Java 发生死锁的根本原因是,在申请锁时发生了交叉闭环申请,synchronized在开发中最好不要嵌套使用,容易导致死锁,需要的朋友可以参考下
    2024-01-01
  • springboot 整合 sa-token简介及入门教程

    springboot 整合 sa-token简介及入门教程

    Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、Session会话、单点登录、OAuth2.0、微服务网关鉴权 等一系列权限相关问题,这篇文章主要介绍了springboot 整合 sa-token简介及入门教程,需要的朋友可以参考下
    2023-05-05
  • 如何将Java对象转换为JSON实例详解

    如何将Java对象转换为JSON实例详解

    有时候需要将对象转换为JSON格式,所以这篇文章主要给大家介绍了关于如何将Java对象转换为JSON的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-08-08
  • Spring整合MyBatis的三种方式

    Spring整合MyBatis的三种方式

    这篇文章主要介绍了Spring整合MyBatis的三种方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01
  • Java实现简单推箱子游戏

    Java实现简单推箱子游戏

    这篇文章主要为大家详细介绍了Java实现推箱子游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-06-06
  • HttpMessageConverter报文信息转换器的深入讲解

    HttpMessageConverter报文信息转换器的深入讲解

    在Spring中内置了大量的HttpMessageConverter,通过请求头信息中的MIME类型,选择相应的HttpMessageConverter,这篇文章主要给大家介绍了关于HttpMessageConverter报文信息转换器的相关资料,需要的朋友可以参考下
    2022-01-01
  • springboot jpa之返回表中部分字段的处理详解

    springboot jpa之返回表中部分字段的处理详解

    这篇文章主要介绍了springboot jpa之返回表中部分字段的处理详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Spring boot + LayIM + t-io 实现文件上传、 监听用户状态的实例代码

    Spring boot + LayIM + t-io 实现文件上传、 监听用户状态的实例代码

    这篇文章主要介绍了Spring boot + LayIM + t-io 实现文件上传、 监听用户状态的实例代码,需要的朋友可以参考下
    2017-12-12
  • Java实现一致性Hash算法详情

    Java实现一致性Hash算法详情

    这篇文章主要介绍了Java实现一致性Hash算法详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09

最新评论