google的google protobuff比这样java原生的方式更有效率吗

3512人阅读
J2EE & SSH(23)
protobuf(3)
  由于需要解析自定义扩展,获取对应的信息,方便以后扩展,需要解析,上网找了很久木有找到解决办法,而且官方的文档(),于是自己在debug模式下一步一步找到解决办法,记录并分享如下。
  注:关于protocol buffer的基础知识,如proto-java.jar文件下载等,请参看上篇博文(【protocol buffers】)。
二、具体解决办法
  1)首先需要生成对应的desc文件,这里包含了要解析的所有内容。
  2)然后根据java相关API解析得到具体的自己所需信息。
  闲话少叙,具体代码如下,代码注释的比较清楚:
package com.test.
import java.io.BufferedR
import java.io.FileInputS
import java.io.InputStreamR
import java.util.HashM
import java.util.L
import java.util.M
import com.google.protobuf.ByteS
import com.google.protobuf.DescriptorProtos.DescriptorP
import com.google.protobuf.DescriptorProtos.FieldDescriptorP
import com.google.protobuf.DescriptorProtos.FileDescriptorP
import com.google.protobuf.DescriptorProtos.FileDescriptorS
import com.google.protobuf.UnknownFieldS
public class ParseProto {
public final String PROTOC_DIR = System.getProperty(&user.dir&) + &/protoc/protoc.exe &; // protoc.exe所在文件夹
public final String PROTO_FILE_DIR = System.getProperty(&user.dir&) + &/protoFile/&; // protoc.exe所在文件夹
* 获取自定义扩展的proto信息
* @param extendDescFileDir 自定义的扩展文件对应的desc文件路径,如options.desc
* @throws Exception
public Map&String, Integer& getExtendInfo(String extendDescFileDir) throws Exception {
Map&String, Integer& result = new HashMap&&();
// 解析desc文件,获取proto文件中的自己扩展
FileDescriptorSet fdSet = FileDescriptorSet.parseFrom(new FileInputStream(extendDescFileDir));
List&FileDescriptorProto& fs = fdSet.getFileList();
for(FileDescriptorProto fdp : fs) {
// 获取自定义的extend扩展中的name-&value
List&FieldDescriptorProto& as = fdp.getExtensionList();
for(FieldDescriptorProto a : as) {
result.put(a.getName(), a.getNumber());
* 根据proto文件获取其中的message信息
* @throws Exception
public Map&String, Object& getMsgInfo(String descFileDir) throws Exception {
Map&String, Object& result = new HashMap&String, Object&();
// 根据得到的desc文件
FileDescriptorSet fdSet = FileDescriptorSet.parseFrom(new FileInputStream(descFileDir));
for(FileDescriptorProto fdp : fdSet.getFileList()) {
// 遍历获取各message信息
for( DescriptorProto dp : fdp.getMessageTypeList()) {
String msgName = dp.getName();
// message名称
String value = &&;
UnknownFieldSet uf = dp.getOptions().getUnknownFields();
for (Map.Entry&Integer, UnknownFieldSet.Field& entry : uf.asMap().entrySet()) {
UnknownFieldSet.Field val = entry.getValue();
value = val.getLengthDelimitedList().get(0).toStringUtf8();
// 如果存在msgId则记录结果
if(!uf.asMap().isEmpty()) {
result.put(msgName, value);
* 生成proto文件描述信息desc
* @param protoName
* @throws Exception
public String genProtoDesc(String protoName) throws Exception{
// 目标文件名
String descFileDir = this.PROTO_FILE_DIR + protoName.replaceAll(&.proto&, &.desc&);
// 命令:protoc -I=$SRC_DIR descriptor_set_out=$DST_DIR/***.desc $SRC_DIR/***.proto
StringBuffer cmd = new StringBuffer();
cmd.append(&cmd /c &);
cmd.append(PROTOC_DIR);
cmd.append(&-I=& + this.PROTO_FILE_DIR).append(& --descriptor_set_out=& + descFileDir).append(& &).append(this.PROTO_FILE_DIR + protoName);
String failMsg = &生成desc文件命令执行失败!&;
execCommand(cmd.toString(), &生成desc描述信息文件&, failMsg);
return descFileD
* 执行cmd控制台命令
* @param cmd
完整命令语句
* @param failMsg 失败时提示语句
* @throws Exception
private void execCommand(String cmd, String execMsg, String failMsg) throws Exception {
// 执行系统命令
System.out.println(&===& + execMsg + &===执行命令:& + cmd);
Runtime run = Runtime.getRuntime();
Process p = run.exec(cmd);
// 如果不正常终止,则生成desc文件失败
if(p.waitFor() != 0) {
if(p.exitValue() == 1) {
System.err.println(failMsg);
// 打印输出的错误信息
BufferedReader in = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line = &&;
while((line = in.readLine()) != null) {
System.out.println(line);
System.out.println();
System.out.println(&命令执行完毕\n&);
执行文件:
package com.test.
import java.util.M
import java.util.Map.E
public class Test {
public static void main(String[] args) throws Exception {
ParseProto parse = new ParseProto();
String desc = parse.genProtoDesc(&options.proto&);
Map&String, Integer& extendInfo = parse.getExtendInfo(desc);
Map&String, Object& msgInfo = parse.getMsgInfo(desc);
System.out.println(&扩展信息:&);
for(Entry&String, Integer& e : extendInfo.entrySet()) {
System.out.println(e.getKey() + &-&& + e.getValue());
System.out.println(&\n协议信息:&);
for(Entry&String, Object& e : msgInfo.entrySet()) {
System.out.println(e.getKey() + &-&& + e.getValue());
三、执行结果截图
  1)项目结构图
  2)需要用到的options.proto
import &google/protobuf/descriptor.proto&;
extend google.protobuf.FileOptions {
optional string my_file_option = 50000;
extend google.protobuf.MessageOptions {
optional string my_option = 55555;
option (my_file_option) = &Hello File world!&;
message MyMessage {
option (my_option) = &Hello world!&;
  3)需要用到的descriptor.proto(),PS可以到官网下载。
(全文完)
注:这个是本人原创的,如需转载,请尊重劳动果实,务必保持原链接()。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:455301次
积分:4883
积分:4883
排名:第3102名
原创:89篇
转载:18篇
评论:237条
(1)(1)(3)(1)(3)(1)(5)(2)(9)(3)(6)(8)(15)(8)(27)(10)(3)(2)Protocol Buffers(protobuf)java初体验 - 推酷
Protocol Buffers(protobuf)java初体验
& & & & 由于项目需要所以简单的研究了下protobuf。我也是参照网上的博客,所以大部分内容我也就不重复造轮子了。首先protobuf介绍点击这里,使用介绍点击这里,使用demo看
。我个人的第一个例子也是参照这个demo来的,不过其中我有遇到一些问题,所以揪出来说说,也就给自己做个笔记,方便查阅。
& & & & 基本的东西相信大家也了解了,直接步入主题了:
& & & & 1、
限定修饰符介绍 required\optional\repeated,之前给定的博客已经有这个介绍了我也不多说,这里把一些小玩儿拿出来讲讲
& & & &①、
required必须的字段,如果不赋值就会抛出&com.google.protobuf.UninitializedMessageException: Message missing required fields: ...异常
& & & &②、
optional可选字段,没什么好说的就是可有可无咯
& & & &③、
repeated可重复的字段可以用来表示数组,在这里我还小小的纠结了会,搞过去就好了(纠结了好一会才知道protobuf数组怎么定义)。其实定义数组很简单repeated string name=字段号;然后在赋值的刚开始用数组的形式来赋值,会抛出java.lang.IndexOutOfBoundsException: Index: 0, Size: 0的异常,我在想size为0,也就是说不能这样搞呀,然后看了下源码是com.google.protobuf.LazyStringList name_ = com.google.protobuf.LazyStringArrayList.EMPTY这样的,也就是说这个玩儿就是个集合嘛,所以赋值就用集合那套来搞定好了
& & & &2、接下来就是正式开始了,首先准备所以需要的protoc.exe和protobuf的jar包,下载点击这里,没什么好配置的,所以开始吧。
& & & &①、首先编写一个proto文件,这里我的文件是test.proto
option java_package = &com.test.protobuf&;
option java_outer_classname = &FirstProtobuf&;
message testBuf
required int32 ID = 1;
optional string Url = 2;
repeated string name=3;
enum PhoneType {
MOBILE = 0;
定义包名什么的就没什么好说的了。
option java_outer_classname = &FirstProtobuf&;定义生成类的名字
message testBuf定义一个message内部类主要的字段什么就在这里面了
required int32 ID = 1; 定义字段,每个字段都必须有一个唯一的字段号,因为我也是初体验所以也不知道这个字段号到底有什么用,但是不写编译就会出Missing field number的错,重复就会出Field number 1 has already been used in &protobuf.testBuf& by field &....&.的错
& & & &②、将文件放在解压的protoc.exe同级目录下
& & & &③、就该编译了,我这里按照网上说的(protoc.exe --java_out=./test.proto)编译出现了问题Missing input file.然后查了下问题解决,
,所以用protoc ./test.proto --java_out=./命令编译通过
& & & &④、把生成的文件拷到新建的java目录下,然后把jar拷贝进来build path下
& & & &⑤、开始测试
序列号数据(封装数据)
public static void main(String[] args) {
FirstProtobuf.testBuf.Builder builder = FirstProtobuf.testBuf.newBuilder();
builder.setID(777);
builder.setName(0, &&);
List&String& values = new ArrayList&String&();
values.add(&aaa&);
values.add(&aba&);
values.add(&baa&);
values.add(&acc&);
builder.addAllName(values);
FirstProtobuf.testBuf info = builder.build();
byte[] result = info.toByteArray();
System.out.println(result.toString());
TestAlone.getData(result);
反序列化数据
public static void getData(byte[] result) {
FirstProtobuf.testBuf testBuf = FirstProtobuf.testBuf.parseFrom(result);
System.out.println(testBuf);
System.out.println(FirstProtobuf.testBuf.PhoneType.HOME);//这里使用枚举
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
好了,到这里初体验完,其实不难的,我刚开始时一直没有理解这个protobuf到底是干什么的,直到我把整个流程跑通了后才理解,它就是一个封装数据(二进制)协议,通过这样的方式封装数据更小,效率更高
已发表评论数()
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
排版有问题
没有分页内容
视频无法显示
图片无法显示对pojo做protobuf的直接编解码实现 - 春晓春晓 - ITeye技术网站
博客分类:
protobuf的编解码具有性能高、传输数据量小、跨语言的特点。但按照protobuf官方文档中说的,需要先用protoc编译器生成对应语言的编解码代理,然后再用代理build和parse数据。
在我的一个应用场景中,使用脚本根据配置直接生成java pojo源码和对应proto文件,并在pojo中加上自身的编解码方法。这样的话若采用调用protoc生成代理的策略显得十分笨拙,另外既然protobuf能用代理类去解编码数据,那理论上,直接编解码数据也是可以实现的,但遗憾的是google并没有提供这方面的文档说明。
经过一番google以后,在stackoverflow上有大神说Descriptor描述proto文件,也有用C++实现的直接编解码的代码,java方面的非常少哇,只有一位朋友分享出来了在此表示十分感激。
下面是编解码代码的实现:
package miniserver.
import static miniserver.util.ReflectionUtil.gatherAllF
import java.io.F
import java.io.FileInputS
import java.io.FileNotFoundE
import java.io.IOE
import java.lang.reflect.F
import java.util.HashM
import java.util.L
import java.util.M
import com.google.protobuf.DescriptorProtos.FileDescriptorP
import com.google.protobuf.DescriptorProtos.FileDescriptorS
import com.google.protobuf.Descriptors.D
import com.google.protobuf.Descriptors.DescriptorValidationE
import com.google.protobuf.Descriptors.FieldD
import com.google.protobuf.Descriptors.FileD
import com.google.protobuf.DynamicM
import com.google.protobuf.DynamicMessage.B
import com.google.protobuf.InvalidProtocolBufferE
import com.google.protobuf.M
public class ProtoParserOrBuilder {
private Map&String, Descriptor& descriptors =
private static final String TEMP_DIR = "D://";
public static final String PROTOC_PATH = System.getProperty("user.dir")
+ "/protoc/protoc.exe";
private File descF
public ProtoParserOrBuilder() {
descriptors = new HashMap&String, Descriptor&();
public ProtoParserOrBuilder(File proto) {
descriptors = new HashMap&String, Descriptor&();
init(proto);
private void init(File proto) {
if (descFile != null && descFile.exists()) {
descFile.delete();
this.descFile = createDescripFile(proto);
FileInputStream fin =
fin = new FileInputStream(descFile);
FileDescriptorSet descriptorSet = FileDescriptorSet.parseFrom(fin);
for (FileDescriptorProto fdp : descriptorSet.getFileList()) {
FileDescriptor fd = FileDescriptor.buildFrom(fdp,
new FileDescriptor[] {});
for (Descriptor descriptor : fd.getMessageTypes()) {
String className = descriptor.getName();
this.descriptors.put(className, descriptor);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (DescriptorValidationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (fin != null) {
fin.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
private File createDescripFile(File proto) {
Runtime run = Runtime.getRuntime();
String descFileName = System.currentTimeMillis()
+ "FastProtoParser.desc";
String protoPath = proto.getCanonicalPath();
String protoFPath = proto.getParentFile().getAbsolutePath();
String cmd = PROTOC_PATH + " -I=" + protoFPath
+ " --descriptor_set_out=" + TEMP_DIR + descFileName + " "
System.out.println(cmd);
// 如果不正常终止, 则生成desc文件失败
Process p = run.exec(cmd);
if (p.waitFor() != 0) {
if (p.exitValue() == 1) {// p.exitValue()==0表示正常结束,1:非正常结束
throw new RuntimeException("protoc 编译器报错");
return new File(TEMP_DIR + descFileName);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
public &T& T parse(Class&T& clazz, byte[] bytes) {
String className = clazz.getSimpleName();
Descriptor desc = this.descriptors.get(className);
Map&String, String& fields = new HashMap&String, String&();
DynamicMessage message = DynamicMessage.parseFrom(desc, bytes);
Map&FieldDescriptor, Object& fieldDescs = message.getAllFields();
for (Map.Entry&FieldDescriptor, Object& entry : fieldDescs
.entrySet()) {
fields.put(entry.getKey().getName(), entry.getValue()
.toString());
T instance = clazz.newInstance();
List&Field& fieldList = ReflectionUtil.gatherAllFields(clazz);
for (Field f : fieldList) {
ReflectionUtil.fillField(fields, instance, f);
} catch (InvalidProtocolBufferException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
public byte[] build(Object obj) {
Class&? extends Object& clazz = obj.getClass();
String className = clazz.getSimpleName();
Descriptor desc = this.descriptors.get(className);
Builder builder = DynamicMessage.newBuilder(desc);
List&FieldDescriptor& fieldDescs = desc.getFields();
List&Field& fields = gatherAllFields(clazz);
Map&String, Object& fieldValues = new HashMap&String, Object&();
for (Field field : fields) {
field.setAccessible(true);
String fieldName = field.getName();
Object fieldValueO
fieldValueObject = field.get(obj);
if (fieldValueObject != null) {
fieldValues.put(fieldName, fieldValueObject);
for (FieldDescriptor fieldDesc : fieldDescs) {
String fieldName = fieldDesc.getName();
Object val = fieldValues.get(fieldName);
if (val != null) {
builder.setField(fieldDesc, val);
Message message = builder.build();
return message.toByteArray();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
protected void finalize() throws Throwable {
super.finalize();
this.descFile.delete();
已经经过验证,与代理类生成的数据是一模一样的,解析也没有问题,请放心使用。
严正声明,本博的原创网站是ITeye,如再有转载请注明出处.谢谢合作.
623deyingxiong
浏览: 97686 次
来自: 北京
* 从protobuf字节数据中,得到指定类 ...
当类中的属性为其它类对象时,会出现转换问题的。
。。。。。。。。。。。。。。。。。。
mark下, 祝作者好运~
xianan3147 写道HI,我看完你的代码,发现有一个问题 ...13:43 提问
求助,Google In-app-Billing接入遇到问题
最近接入Googleplay支付,求一个可以使用的demo源码。网上下的几个,配置为自己的package,publickey,productID后仍不能支付成功,显示您需要登录自己的Googleplay账户,但是该手机上的其他游戏是可以正常支付的。
!使用demo时的错误提示]()
其他相似问题
相关参考资料

我要回帖

更多关于 google protobuf 的文章

 

随机推荐