c ++ protobuf:如何遍历消息字段?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23963978/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
c++ protobuf: how to iterate through fields of message?
提问by Dania
I'm new to protobuf and I'm stuck with simple task: I need to iterate through fields of message and check it's type. If type is message I will do same recursively for this message.
我是 protobuf 的新手,我被简单的任务困住了:我需要遍历消息字段并检查它的类型。如果类型是消息,我将对此消息递归执行相同的操作。
For example, I have such messages:
例如,我有这样的消息:
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;
}
Now, to explicitly access a field value in C++ I can do this:
现在,要在 C++ 中显式访问字段值,我可以这样做:
MyTool::Configuration config;
fstream input("config", ios::in | ios::binary);
config.ParseFromIstream(&input);
bool option1val = config.globalSettings().option1();
bool option2val = config.globalSettings().option2();
and so on. This approach is not convenient in case when have big amount of fields.
等等。这种方法在有大量字段的情况下不方便。
Can I do this with iteration and get field's name and type? I know there are descriptorsof type and somewhat called reflection, but I didn't have success in my attempts. Can some one give me example of code if it's possible?
我可以通过迭代来做到这一点并获取字段的名称和类型吗?我知道有类型的描述符,有点被称为反射,但我的尝试没有成功。如果可能的话,有人可以给我代码示例吗?
Thanks!
谢谢!
采纳答案by Kenton Varda
Take a look at how the Protobuf library implements the TextFormat::Printer
class, which uses descriptors and reflection to iterate over fields and convert them to text:
看看 Protobuf 库是如何实现这个TextFormat::Printer
类的,它使用描述符和反射来迭代字段并将它们转换为文本:
https://github.com/google/protobuf/blob/master/src/google/protobuf/text_format.cc#L1473
https://github.com/google/protobuf/blob/master/src/google/protobuf/text_format.cc#L1473
回答by M.C.
This is old but maybe someone will benefit. Here is a method that prints the contents of a protobuf message:
这是旧的,但也许有人会受益。这是一个打印 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());
}
}
You can find in FieldDescriptor Enum Valuesthe possible values you get from field->type
. For example for the message type you would have to check if type is equal to FieldDescriptor::TYPE_MESSAGE
.
您可以在FieldDescriptor Enum Values 中找到您从 中获得的可能值field->type
。例如,对于消息类型,您必须检查 type 是否等于FieldDescriptor::TYPE_MESSAGE
。
This function prints all the "metadata" of the protobuf message. However you need to check separately for each value what the type is and then call the corresponding getter function using Reflection.
此函数打印 protobuf 消息的所有“元数据”。但是,您需要分别检查每个值的类型,然后使用Reflection调用相应的 getter 函数。
So using this condition we could extract the strings :
所以使用这个条件我们可以提取字符串:
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());
}
However fields can be either repeated or not repeated and different methods are used for both field types. So a check is used here to assure that we are using the right method. For repeated fields we have for example this method for strings :
然而,字段可以重复或不重复,两种字段类型使用不同的方法。因此,这里使用检查来确保我们使用的是正确的方法。对于重复的字段,我们有例如字符串的这种方法:
GetRepeatedString(const Message & message, const FieldDescriptor * field, int index
)
GetRepeatedString(const Message & message, const FieldDescriptor * field, int index
)
So it takes the index of the repeated field into consideration.
所以它考虑了重复字段的索引。
In the case of FieldDescriptor of type Message, the function provided will only print the name of the message, we better print its contents too.
对于 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);
}
}
And finally if the field is repeated you will have to call the FieldSize
method on the reflection and iterate all repeated fields.
最后,如果该字段重复,您将不得不FieldSize
在反射上调用该方法并迭代所有重复的字段。