对pojo做protobuf的直接编解码实现

[list]
protobuf的编解码具有性能高、传输数据量小、跨语言的特点。但按照protobuf官方文档[url]https://developers.google.com/protocol-buffers/docs/overview[/url]中说的,需要先用protoc编译器生成对应语言的编解码代理,然后再用代理build和parse数据。
[/list]
[list]
在我的一个应用场景中,使用脚本根据配置直接生成java pojo源码和对应proto文件,并在pojo中加上自身的编解码方法。这样的话若采用调用protoc生成代理的策略显得十分笨拙,另外既然protobuf能用代理类去解编码数据,那理论上,直接编解码数据也是可以实现的,但遗憾的是google并没有提供这方面的文档说明。
[/list]
[list]
经过一番google以后,在stackoverflow上有大神说Descriptor描述proto文件,也有用C++实现的直接编解码的代码,java方面的非常少哇,只有一位朋友分享出来了[url]http://blog.csdn.net/lufeng20/article/details/8736584[/url]在此表示十分感激。
[/list]

下面是编解码代码的实现:

package miniserver.util;

import static miniserver.util.ReflectionUtil.gatherAllFields;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.DescriptorValidationException;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.DynamicMessage;
import com.google.protobuf.DynamicMessage.Builder;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;

public class ProtoParserOrBuilder {
private Map<String, Descriptor> descriptors = null;
private static final String TEMP_DIR = "D://";
public static final String PROTOC_PATH = System.getProperty("user.dir")
+ "/protoc/protoc.exe";
private File descFile;

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 = null;
try {
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 {
try {
if (fin != null) {
fin.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

private File createDescripFile(File proto) {
try {
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 + " "
+ protoPath;
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();
}
return null;

}

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>();
try {
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);
}

return instance;
} 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();
}

return null;

}

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);
try {
Map<String, Object> fieldValues = new HashMap<String, Object>();
for (Field field : fields) {
field.setAccessible(true);
String fieldName = field.getName();
Object fieldValueObject;
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();
}
return null;
}

@Override
protected void finalize() throws Throwable {
super.finalize();

this.descFile.delete();
}

}

已经经过验证,与代理类生成的数据是一模一样的,解析也没有问题,请放心使用。

严正声明,本博的原创网站是ITeye,如再有转载请注明出处.谢谢合作.
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值