簡體   English   中英

c ++ protobuf:如何遍歷消息字段?

[英]c++ protobuf: how to iterate through fields of message?

我是protobuf的新手,我遇到了簡單的任務:我需要遍歷消息字段並檢查它的類型。 如果type是message,我將以遞歸方式為此消息執行相同操作。

例如,我有這樣的消息:

package MyTool;

message Configuration {
    required GloablSettings         globalSettings  = 1;
    optional string                 option1         = 2;
    optional int32                  option2         = 3;
    optional bool                   option3         = 4;

}

message GloablSettings {
    required bool                   option1         = 1;
    required bool                   option2         = 2;
    required bool                   option3         = 3;
}

現在,要在C ++中顯式訪問字段值,我可以這樣做:

MyTool::Configuration config;
fstream input("config", ios::in | ios::binary);
config.ParseFromIstream(&input);

bool option1val = config.globalSettings().option1();
bool option2val = config.globalSettings().option2();

等等。 當有大量字段時,這種方法不方便。

我可以通過迭代執行此操作並獲取字段的名稱和類型嗎? 我知道有類型的描述符 ,有些叫反射 ,但我的嘗試沒有成功。 有人可以給我一些代碼示例嗎?

謝謝!

這是舊的,但也許有人會受益。 這是一個打印protobuf消息內容的方法:

 void Example::printMessageContents(std::shared_ptr<google::protobuf::Message> m)
 {
      const Descriptor *desc       = m->GetDescriptor();
      const Reflection *refl       = m->GetReflection();   
      int fieldCount= desc->field_count();
      fprintf(stderr, "The fullname of the message is %s \n", desc->full_name().c_str());
      for(int i=0;i<fieldCount;i++)
      {
        const FieldDescriptor *field = desc->field(i);
        fprintf(stderr, "The name of the %i th element is %s and the type is  %s \n",i,field->name().c_str(),field->type_name());
      }
 } 

您可以在FieldDescriptor Enum Values中找到從field->type獲得的可能值。 例如,對於消息類型,您必須檢查類型是否等於FieldDescriptor::TYPE_MESSAGE

此功能打印protobuf消息的所有“元數據”。 但是,您需要分別檢查每個值的類型,然后使用Reflection調用相應的getter函數。

所以使用這個條件我們可以提取字符串:

 if(field->type() == FieldDescriptor::TYPE_STRING  && !field->is_repeated())
      {
            std::string g= refl->GetString(*m, field);
            fprintf(stderr, "The value is %s ",g.c_str());
      }

但是,可以重復或不重復字段,並且對兩種字段類型使用不同的方法。 因此,這里使用檢查來確保我們使用正確的方法。 對於重復的字段,我們有例如字符串的這種方法:

GetRepeatedString(const Message & message, const FieldDescriptor * field, int index

因此需要考慮重復場的索引。

對於Message類型的FieldDescriptor,提供的功能只會打印消息的名稱,我們也會更好地打印它的內容。

      if(field->type()==FieldDescriptor::TYPE_MESSAGE)
       {
         if(!field->is_repeated())  
         {
           const Message &mfield = refl->GetMessage(*m, field);      
           Message *mcopy = mfield.New();
           mcopy->CopyFrom(mfield);
           void *ptr = new std::shared_ptr<Message>(mcopy);
           std::shared_ptr<Message> *m =
           static_cast<std::shared_ptr<Message> *>(ptr);
           printMessageContents(*m);
          }
       }

最后,如果重復該字段,則必須在反射上調用FieldSize方法並迭代所有重復的字段。

看一下Protobuf庫如何實現TextFormat::Printer類,它使用描述符和反射迭代字段並將它們轉換為文本:

https://github.com/google/protobuf/blob/master/src/google/protobuf/text_format.cc#L1473

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM