`
623deyingxiong
  • 浏览: 190133 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

阅读更多


    在我的一个应用场景中,使用脚本根据配置直接生成java pojo源码和对应proto文件,并在pojo中加上自身的编解码方法。这样的话若采用调用protoc生成代理的策略显得十分笨拙,另外既然protobuf能用代理类去解编码数据,那理论上,直接编解码数据也是可以实现的,但遗憾的是google并没有提供这方面的文档说明。


    经过一番google以后,在stackoverflow上有大神说Descriptor描述proto文件,也有用C++实现的直接编解码的代码,java方面的非常少哇,只有一位朋友分享出来了http://blog.csdn.net/lufeng20/article/details/8736584在此表示十分感激。


下面是编解码代码的实现:
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
0
分享到:
评论
3 楼 java_pad 2016-01-08  
ReflectionUtil 是个什么东东???能否也贴出来呢?
2 楼 623deyingxiong 2014-06-17  

	/**
	 * 从protobuf字节数据中,得到指定类型的对象实例.
	 * 
	 * @param clazz
	 * @param bytes
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T parse(Class<T> clazz, byte[] bytes) {
		String simpleName = clazz.getSimpleName();
		Descriptor desc = this.descriptors.get(simpleName);
		try {
			return (T) parseMessage(simpleName,
					DynamicMessage.parseFrom(desc, bytes));
		} catch (InvalidProtocolBufferException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	private Object parseMessage(String className, DynamicMessage message) {
		Map<String, Object> fields = new HashMap<String, Object>();
		try {
			Map<FieldDescriptor, Object> fieldDescs = message.getAllFields();
			for (Map.Entry<FieldDescriptor, Object> entry : fieldDescs
					.entrySet()) {
				if (ReflectionUtil.isPrimitive(entry.getValue().getClass())) {// 基础类型和
																				// String
					fields.put(entry.getKey().getName(), entry.getValue()
							.toString());
				} else if (entry.getValue().getClass() == DynamicMessage.class) {// 消息类型
					DynamicMessage subMessage = (DynamicMessage) entry
							.getValue();
					String subClassName = subMessage.getDescriptorForType()
							.getName();
					fields.put(entry.getKey().getName(),
							parseMessage(subClassName, subMessage));

				} else {// 其他类型属性

				}
			}

			Class<?> clazz = Class.forName(getModelClassName(className));
			Object instance = clazz.newInstance();

			List<Field> fieldList = ReflectionUtil.gatherAllFields(clazz);
			for (Field f : fieldList) {
				ReflectionUtil.fillFieldAnyType(fields, instance, f);
			}

			return instance;
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;

	}

	

	/**
	 * 将对象实例转换成protobuf字节数据
	 * 
	 * 注意,对于pojo引用类型的属性,一定要定义在model包之下,否则不予处理.
	 * 
	 * @param obj
	 * @return
	 */
	public byte[] build(Object obj) {
		return buildMessage(obj).toByteArray();
	}

	private Message buildMessage(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) {
					if (ReflectionUtil.isPrimitive(val.getClass())) {// 对于基础类型和
																		// String
						builder.setField(fieldDesc, val);
					} else if (Utils.isCustomizedModel(val.getClass())) {// 对于 model
																	// 类型
						builder.setField(fieldDesc, buildMessage(val));
					} else {// TODO ENUM, I guess.

					}
				}
			}

			return builder.build();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;

	}

这个可以,不过你需要根据自己的场景做适当调整,如果你有更多的扩展希望你能把你的版本分享给我.
1 楼 shadowsese 2014-05-29  
当类中的属性为其它类对象时,会出现转换问题的。

相关推荐

    第七章:编解码器Codec.pdf

    编写一个网络应用程序需要实现某种编解码器,编解码器的作用就是讲原始字节数据与自定义的消息对象进行互转。网络中都是以字节码的数据形式来传输数据的,服务器编码数据后发送到客户端,客户端需要对数据进行解码。...

    基于Java的通用TCP/串口字节流编解码器设计源码

    包含59个文件,主要包括54个Java源文件,其余文件包括Git忽略规则、许可证、Markdown文档、XML配置文件、处理器配置等,旨在实现对各类字节流数据的编解码,支持对POJO对象的序列化和反序列化操作,适用于TCP、串口...

    java Pojo转Map

    本文将详细介绍如何实现Java中的Pojo到Map的转换,并通过具体的示例来演示这一过程。 首先,我们需要一个Pojo类,例如: ```java public class User { private String name; private int age; // getters and ...

    JDBCTemplate+JavaPOJO实现通用DAO

    在Java开发中,数据访问对象(DAO)模式是一种常见的设计模式,用于封装对数据库的操作,使得业务逻辑与数据访问逻辑分离。JDBC(Java Database Connectivity)是Java平台中用于访问数据库的标准API,但它直接使用...

    POJO In action 代码

    POJO的概念源自于对早期企业级Java应用中过度复杂性的反叛,尤其是EJB(Enterprise JavaBeans)带来的繁琐。POJOs强调的是简单性,它们是普通的Java类,不遵循任何特定的框架或标准,允许开发者自由地进行设计和实现...

    Mybatis生成pojo插件

    6. 点击"Generate",插件将自动生成包含字段、getter/setter方法和无参构造函数的POJO类,这些类可以直接用于Mybatis的Mapper文件中。 这个插件的使用极大地减少了手动编写POJO类的工作量,使得开发者可以更专注于...

    数据表生成pojo

    - 关联关系:如果有外键关联,POJO类可能需要包含对其他POJO类的引用,或者使用集合来表示一对多或多对多的关系。 5. **ORM框架中的POJO应用** - Hibernate:在Hibernate中,POJO作为持久化对象,通过注解或XML...

    利用反射技术实现POJO值拷贝

    本篇文章将探讨如何利用反射技术实现POJO(Plain Old Java Object)值的拷贝,这对于数据传输、对象复制等场景非常有用。 POJO是一个简单的Java对象,没有特定的框架限制,通常包含一组属性和对应的getter/setter...

    SSM笔记-POJO

    同时,为了防止空指针异常,我们还可以使用`@Valid`注解配合`BindingResult`来对POJO进行校验。这允许我们在处理请求之前验证输入的数据,确保其符合业务规则。 例如: ```java @PostMapping("/register") public ...

    POJO转Map.txt

    #### 三、POJO转Map实现原理 POJO转Map的基本思路是利用反射机制获取POJO的所有属性,并将其存储到Map中。具体步骤如下: 1. **判断对象是否为空**:首先检查传入的对象是否为空,如果为空则直接返回null。 2. **...

    SSM项目实战pojo类

    4. **注解**:在SSM项目中,MyBatis的注解可以被用来直接在POJO类上声明SQL映射。例如,我们可以使用`@Table`声明表名,`@Id`标识主键,`@Column`指定列名等。这简化了配置,使得开发更为便捷: ```java import ...

    json2pojo功能有点麻烦

    参照https://github.com/joelittlejohn/jsonschema2pojo/wiki/Getting-Started的步骤Using jsonschema2pojo within your Java project (embedded)

    hibernate tools 生成pojo

    源码层面,Hibernate Tools是开源的,开发者可以深入研究其生成POJO的实现过程,理解其工作原理,这对于优化自定义的代码生成规则或是扩展工具功能非常有帮助。 **六、总结** 总的来说,Hibernate Tools是Java开发...

    用WebService传递POJO和对象数组的例子.doc

    在Web服务上下文中,POJO可以被直接映射为服务的输入和输出参数,使得服务接口更直观、更易于理解。 在AXIS2中,我们可以直接将POJO类转换为Web服务,这是一种自底向上的开发模式。在这个例子中,`sample.pojo.data...

    pojo生成器

    生成的这些类可以被应用到ORM框架中,实现对数据库的CRUD(创建、读取、更新、删除)操作。 总结来说,这个“pojo生成器”是一个针对Oracle数据库的实用工具,能够帮助Java开发者快速生成符合ORM需求的Java类,减轻...

    pojo类与javaBean

    一个典型的POJO类通常只包含属性以及相应的getter和setter方法,但并不限制其实现业务逻辑或其他功能。 **JavaBean**,是一种遵循特定规范的Java类,最初由Sun Microsystems(现在是Oracle的一部分)提出,旨在用于...

    实体类POJO生成器

    对于复杂的数据库设计,如包含外键关联的表,生成器可能还会生成对应的关联对象,比如一对多、多对一、多对多关系。在处理这类关系时,生成器会生成对应的List、Set或单个对象引用,以满足Java对象模型的构建。 在...

    Eclipse中新建SpringBoot项目完成对json、pojo、map、list的请求源码

    处理Map请求时,`@RequestBody Map, String&gt;`可以直接接收键值对形式的JSON数据。处理List请求时,`@RequestBody List&lt;MyPojo&gt;`可以接收一个MyPojo对象的数组或列表。 为了实现这些功能,Spring Boot依赖于Jackson...

Global site tag (gtag.js) - Google Analytics