问题记录:
1、在使用protobuf反射机制动态加载解析proto文件时,发现当proto文件中含有import系统proto文件的语句时,无法解析文件,解决方法是添加路径映射。
1 google::protobuf::compiler::DiskSourceTree sourceTree; 2 sourceTree.MapPath("data", "./data"); 3 sourceTree.MapPath("", "D:\Documents\Program\Tools\protobuf-3.0.2\install\x86\debug\include"); 4 google::protobuf::compiler::Importer importer(&sourceTree, NULL); 5 const google::protobuf::FileDescriptor *fileDescriptor = importer.Import("data/test.proto");
代码如上,其中的第3行为解决方案,增加之后才能正确解析。分析其原因是,Importer对象用于导入并解析proto文件,当proto文件中import了其他proto文件时,Importer对象递归导入并解析该proto文件;第二行告诉了Importer去哪里找test.proto,但是却没有告诉Importer去哪里找系统自带的proto文件,因此需要加上第3行,并且别名应该留空!
2、jsoncpp的下载和使用
jsoncpp源码可以从github上得到:jsoncpp-master.zip
解压后使用python执行根目录下的 amalgamate.py ,这个脚本将jsoncpp的头文件和源代码进行了合并,最终合并成了三个文件:
distjsonjson.h distjsonjson-forwards.h distjsoncpp.cpp
使用时把 jsoncpp.cpp文件连同json文件夹一起拷贝到工程目录下,两者保持同级,代码中包含 jsonjson.h 即可。
3、遍历proto文件中的所有消息以及所有字段
1 #include <iostream> 2 #include <google/protobuf/compiler/importer.h> 3 #include <google/protobuf/dynamic_message.h> 4 #include <google/protobuf/util/json_util.h> 5 6 int parseProtoFile() 7 { 8 // 准备配置好文件系统 9 google::protobuf::compiler::DiskSourceTree sourceTree; 10 // 将当前路径映射为项目根目录 , project_root 仅仅是个名字,你可以你想要的合法名字. 11 sourceTree.MapPath("data", "./data"); 12 sourceTree.MapPath("", "D:\Documents\Tools\protobuf-3.0.2\install\x86\debug\include"); 13 // 配置动态编译器. 14 google::protobuf::compiler::Importer importer(&sourceTree, NULL); 15 // 动态编译proto源文件。 源文件在./source/proto/test.proto . 16 auto fileDescriptor = importer.Import("data/complex.proto"); 17 18 19 std::cout << fileDescriptor->message_type_count() << std::endl; 20 for (auto i = 0; i < fileDescriptor->message_type_count(); i++) 21 { 22 auto descriptor = fileDescriptor->message_type(i); 23 24 std::cout << descriptor->name() << " " << descriptor->field_count() << " " << descriptor->nested_type_count() << std::endl; 25 26 auto descriptor1 = descriptor->containing_type(); 27 28 if (descriptor1) 29 { 30 std::cout << descriptor1->name() << std::endl; 31 } 32 } 33 34 std::cout << fileDescriptor->name() << std::endl; 35 36 37 auto descriptor = fileDescriptor->message_type(1); 38 for (auto i = 0; i < descriptor->field_count(); i++) 39 { 40 auto fieldDes = descriptor->field(i); 41 google::protobuf::SourceLocation outLocation; 42 if (fieldDes->GetSourceLocation(&outLocation)) 43 { 44 printf("%s: %d %d %d %d leading_comments:%s trailing_comments:%s ", 45 fieldDes->full_name().c_str(), 46 outLocation.start_line, outLocation.start_column, outLocation.end_line, outLocation.end_column, 47 outLocation.leading_comments.c_str(), outLocation.trailing_comments.c_str()); 48 for (auto comment : outLocation.leading_detached_comments) 49 { 50 printf("leading_detached_comments:%s ", comment.c_str()); 51 } 52 } 53 else 54 { 55 std::cout << "fail" << std::endl; 56 } 57 } 58 59 #if 0 60 // 现在可以从编译器中提取类型的描述信息. 61 auto descriptor1 = importer.pool()->FindMessageTypeByName("T.Test.InMsg"); 62 63 // 创建一个动态的消息工厂. 64 google::protobuf::DynamicMessageFactory factory; 65 // 从消息工厂中创建出一个类型原型. 66 auto proto1 = factory.GetPrototype(descriptor1); 67 // 构造一个可用的消息. 68 auto message1 = proto1->New(); 69 // 下面是通过反射接口给字段赋值. 70 auto reflection1 = message1->GetReflection(); 71 auto filed1 = descriptor1->FindFieldByName("id"); 72 reflection1->SetUInt32(message1, filed1, 1); 73 74 // 打印看看 75 std::cout << message1->DebugString() << std::endl; 76 77 std::string output; 78 google::protobuf::util::MessageToJsonString(*message1, &output); 79 std::cout << output << std::endl; 80 81 // 删除消息. 82 delete message1; 83 #endif 84 return 0; 85 } 86 87 #define Log(format, ...) printf(format, __VA_ARGS__) 88 89 void printOneField(const google::protobuf::FieldDescriptor *fieldDescriptor) 90 { 91 Log(" field[%d]: name %s, full name %s, json name %s, type %s, cpp type %s ", 92 fieldDescriptor->index(), fieldDescriptor->name().c_str(), fieldDescriptor->full_name().c_str(), fieldDescriptor->json_name().c_str(), 93 fieldDescriptor->type_name(), fieldDescriptor->cpp_type_name()); 94 Log(" debug string:%s ", fieldDescriptor->DebugString().c_str()); 95 } 96 97 void printOneMessage(const google::protobuf::Descriptor *descriptor) 98 { 99 // 消息的总体信息 100 Log("msg[%d]: name %s, full name %s, field count %d, nested type count %d ", 101 descriptor->index(), descriptor->name().c_str(), descriptor->full_name().c_str(), descriptor->field_count(), 102 descriptor->nested_type_count()); 103 Log(" debug string: %s ", descriptor->DebugString().c_str()); 104 105 // 遍历消息的所有字段 106 for (int fieldLoop = 0; fieldLoop < descriptor->field_count(); fieldLoop++) 107 { 108 const google::protobuf::FieldDescriptor *fieldDescriptor = descriptor->field(fieldLoop); 109 110 printOneField(fieldDescriptor); 111 } 112 113 // 遍历消息的所有嵌套消息 114 for (int nestedLoop = 0; nestedLoop < descriptor->nested_type_count(); nestedLoop++) 115 { 116 const google::protobuf::Descriptor *nestedDescriptor = descriptor->nested_type(nestedLoop); 117 118 printOneMessage(nestedDescriptor); 119 } 120 } 121 122 void printOneFile(const google::protobuf::FileDescriptor *fileDescriptor) 123 { 124 Log("******** message info in proto file, msg count %d ******** ", fileDescriptor->message_type_count()); 125 126 // 遍历文件中的所有顶层消息 127 for (int msgLoop = 0; msgLoop < fileDescriptor->message_type_count(); msgLoop++) 128 { 129 const google::protobuf::Descriptor *descriptor = fileDescriptor->message_type(msgLoop); 130 131 printOneMessage(descriptor); 132 } 133 } 134 135 bool testProto(const char *protoIncludePath, const char *testProtoPath, const char *testProtoFile) 136 { 137 // 配置文件系统 138 google::protobuf::compiler::DiskSourceTree sourceTree; 139 sourceTree.MapPath("", protoIncludePath); 140 sourceTree.MapPath("data", testProtoPath); 141 //sourceTree.MapPath("data", "./data"); 142 //sourceTree.MapPath("", "D:\Documents\Tools\protobuf-3.0.2\install\x86\debug\include"); 143 // 配置动态编译器 144 google::protobuf::compiler::Importer importer(&sourceTree, NULL); 145 // 动态编译proto源文件 146 const google::protobuf::FileDescriptor *fileDescriptor = importer.Import("data/" + std::string(testProtoFile)); 147 148 if (fileDescriptor == NULL) 149 { 150 printf("import "%s" failed, last error msg: %s ", testProtoFile, sourceTree.GetLastErrorMessage().c_str()); 151 return false; 152 } 153 154 printOneFile(fileDescriptor); 155 156 return true; 157 } 158 159 int main() 160 { 161 const char *protoIncludePath = "D:\Documents\Tools\protobuf-3.0.2\install\x86\debug\include"; 162 const char *testProtoPath = "C:\Users\Administrator\Desktop\Document\C++\protobufTest\protobufTest\data"; 163 const char *testProtoFile = "complex.proto"; 164 165 testProto(protoIncludePath, testProtoPath, testProtoFile); 166 167 //parseProtoFile(); 168 //printf("Hello world! "); 169 return 0; 170 }