使用protobuf的反射来动态生成Message并赋值

前言:

公司做了个项目,使用protobuf,各功能号会不断增加,message也会一直变,我做为测试人员,需要做一个测试工具,前期可以通过mfc画图,就是画出message的各个属性对应的输入框,之后按照格式,遍历对应的message,读取输入框的数据来一个个赋值。这样一来,功能号少的时候,还没什么,多画点输入框就是了。后来,message嵌套越来越复杂,其中还夹杂着repeated的属性,repeated中还有repeated、子message,直接崩溃。于是就决心查资料做了这个动态生成的,用CMarku从xml读取数据,感觉挺好用,留个档,也希望对其他人能有个帮助。

预期读者:

对protobuf有一定了解,至少已经跑通过程序,只是遇到了和我类似的问题,现在要使用protobuf的动态生成message并赋值、发送消息给服务端。

理论方面,可以查看大牛的http://blog.csdn.net/solstice/article/details/6300108介绍,说的很细。

正文:

如果我在这里大谈理论,可能就是班门弄斧了,那么废话不多说,直接上代码,上核心代码。

根据message name来生成message


Message* createMessage(const std::string& typeName)
{
    google::protobuf::Message* message = NULL;
    const google::protobuf::Descriptor* descriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(typeName);
    if (descriptor)
    {
        const google::protobuf::Message* prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
        if (prototype)
        {
            message = prototype->New();
        }
    }
    return message;
}

typeName就是下面定义的 Role

message Role
{  
  required int32 RoleId= 1;   
  required bytes RoleName= 2;

  required string Status= 3; 

repeated Permission p=4;

}

message Permission

{

required id=1;

required pname=XXX;

}

总结一下,我是这么理解的,如要根据message name动态生成message,先生成message的descriptor,再根据descriptor生成Message类,类再new一个message对象,即可使用。有理解的不对的地方,请大家指教。关于message中的属性项,如上面Role的RoleId、RoleName、Status,也是一个流程,只是descriptor变成了field 的descriptor等。

由于message内部属性的各种嵌套repeated message,而要给一个message对象的属性赋值,使用Role的反射只能赋值一层,比如上面例子Role,只能给message  ROle 的RoleId、RoleName、Status、p赋值,但是不能为Permission的id、pname赋值,如果要给id、pname赋值,需要使用Permission的反射,道理同Role。这里还涉及到一个repeated,先说反射,再来说这个repeated的处理。

pMsg=createMessage(packageName);
                pDescriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(packageName);
                pReflection = pMsg->GetReflection();

上面的pMsg、pDescriptor、pReflection在嵌套赋值的使用要用到,先不考虑冗余,暂且弄个

struct returnStruct
{
    const google::protobuf::Descriptor* pDescriptor;
    const google::protobuf::Reflection* pReflection;
    google::protobuf::Message* pMsg;
};

struct returnStruct re={pDescriptor,pReflection,pMsg}

参照后面的代码,可能会好理解一点。大概说一下,把pDescriptor,pReflection,pMsg搜集起来,在对Role属性赋值的时候,带上这个returnStruct,这样在子message Permission的赋值处理时候,Permission赋值完毕,可以直接使用returnStruct的Permission的父级Role的反射来把Permission对象赋值给Role的p,递归的时候特别好用。

pFieldDescriptor=re->pDescriptor->FindFieldByName(tag_name);//tag_name 是属性项的名字,就是Role中的RoleId、RoleName之类。

        if(pFieldDescriptor->is_repeated()){//考虑到googel对protobuf 中属性为repeated的赋值接口不同,这里分开
                switch(pFieldDescriptor->type())
                {
                case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
                    re->pReflection->AddDouble(re->pMsg,pFieldDescriptor,atof(tag_value.c_str()));
                    break;
                case google::protobuf::FieldDescriptor::TYPE_FLOAT:
                    re->pReflection->AddFloat(re->pMsg,pFieldDescriptor,atof(tag_value.c_str()));
                    break;

                  .........

                 case google::protobuf::FieldDescriptor::TYPE_MESSAGE://嵌套的message的处理
                    {
                        pDescriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(packageName);
                        if(xml.IntoElem())   //xml是CMarkUP的xml当前节点的读取位置,xml数据格式和message.proto文件一模一样,当前情况相当于对Role的前三项赋值完毕,来处理permission项
                        {   
                            pMsg=re->pReflection->AddMessage(re->pMsg,pFieldDescriptor);//注意这里是AddMessage,和下面的MutableMessage对应,处理repeated类型的
                            pReflection = pMsg->GetReflection();
                            struct returnStruct ret={pDescriptor,pReflection,pMsg};
                            parseXml(xml,pTestLogger,"",&ret);    
                            xml.OutOfElem();
                        }
                        break;
                    }

                }

}else //属性项不是repeated的处理

{

//这里主要就是个接口不同

....

pMsg=re->pReflection->MutableMessage(re->pMsg,pFieldDescriptor);//注意这里是MutableMessage

....

}


今天股票大跌,我已经么有心思再多写什么了,有什么问题,可以问,吃面吧

你可能感兴趣的:(C++)