【protocol buffers】java解析protoBuf custom option自定义扩展文件具体方法

一、前言

  由于需要解析自定义扩展,获取对应的信息,方便以后扩展,需要解析,上网找了很久木有找到解决办法,而且官方的文档(https://developers.google.com/protocol-buffers/docs/reference/java/index),于是自己在debug模式下一步一步找到解决办法,记录并分享如下。

  注:关于protocol buffer的基础知识,如proto-java.jar文件下载等,请参看上篇博文(【protocol buffers】java解析.proto文件具体方法)。

二、具体解决办法

  1)首先需要生成对应的desc文件,这里包含了要解析的所有内容。

  2)然后根据java相关API解析得到具体的自己所需信息。

  闲话少叙,具体代码如下,代码注释的比较清楚:

package com.test.proto;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.protobuf.ByteString;
import com.google.protobuf.DescriptorProtos.DescriptorProto;
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
import com.google.protobuf.UnknownFieldSet;

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
	 * @return
	 * @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());
			}
		}
		
		return result;
	}
	
	/**
	 * 根据proto文件获取其中的message信息
	 * @return
	 * @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);
				}
			}
		}
		
		return result;
	}
	
	/**
	 * 生成proto文件描述信息desc
	 * @param protoName
	 * @return
	 * @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 descFileDir;
	}
	
	/**
	 * 执行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.proto;

import java.util.Map;
import java.util.Map.Entry;

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

}


三、执行结果截图

【protocol buffers】java解析protoBuf custom option自定义扩展文件具体方法


四、附件

  1)项目结构图

【protocol buffers】java解析protoBuf custom option自定义扩展文件具体方法

  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可以到官网下载。

  


(全文完)

注:这个是本人原创的,如需转载,请尊重劳动果实,务必保持原链接(http://blog.csdn.net/lufeng20/article/details/18276557)。

【protocol buffers】java解析protoBuf custom option自定义扩展文件具体方法

上一篇:erlang编程的那些事儿


下一篇:通用makefile是如何炼成的(XI)--最后的完结篇,envsetup.sh