1. 程式人生 > >google protobuf 反射的例子

google protobuf 反射的例子

反射就是可以知道一個物件自身的資料屬性,例如有哪些欄位,欄位的值,函式等等。
下面的程式碼展示了protobuf 物件反射的例子。將一個物件按照反射的欄位順序序列化到string,然後反序列化到物件。最後呼叫反射列印其欄位值,可以看到物件能夠還原。

proto檔案為(暫時不支援陣列):

package tutorial;

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  optional PhoneNumber phone = 4;
  
}
#include <string>
#include <map>
#include <iostream>
#include <stdio.h>
#include "person.pb.h"
using namespace tutorial;
using namespace google::protobuf;
using std::cout;
using std::endl;
using std::string;


void serialize_message(const Message& message, std::string* serialized_string) {
    const Descriptor* descriptor = message.GetDescriptor();
    const Reflection* reflection = message.GetReflection();

    for (int i = 0; i < descriptor->field_count(); ++i) {
        const FieldDescriptor* field = descriptor->field(i);
        bool has_field = reflection->HasField(message, field);

        if (has_field) {
            //arrays not supported
            assert(!field->is_repeated());

            switch (field->cpp_type()) {
#define CASE_FIELD_TYPE(cpptype, method, valuetype)\
                case FieldDescriptor::CPPTYPE_##cpptype:{\
                    valuetype value = reflection->Get##method(message, field);\
                    int wsize = field->name().size();\
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));\
                    serialized_string->append(field->name().c_str(), field->name().size());\
                    wsize = sizeof(value);\
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));\
                    serialized_string->append(reinterpret_cast<char*>(&value), sizeof(value));\
                    break;\
                }

                CASE_FIELD_TYPE(INT32, Int32, int);
                CASE_FIELD_TYPE(UINT32, UInt32, uint32_t);
                CASE_FIELD_TYPE(FLOAT, Float, float);
                CASE_FIELD_TYPE(DOUBLE, Double, double);
                CASE_FIELD_TYPE(BOOL, Bool, bool);
                CASE_FIELD_TYPE(INT64, Int64, int64_t);
                CASE_FIELD_TYPE(UINT64, UInt64, uint64_t);
#undef CASE_FIELD_TYPE
                case FieldDescriptor::CPPTYPE_ENUM: {
                    int value = reflection->GetEnum(message, field)->number();
                    int wsize = field->name().size();
                    //寫入name佔用位元組數
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    //寫入name
                    serialized_string->append(field->name().c_str(), field->name().size());
                    wsize = sizeof(value);
                    //寫入value佔用位元組數
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    //寫入value
                    serialized_string->append(reinterpret_cast<char*>(&value), sizeof(value));
                    break;
                }
                case FieldDescriptor::CPPTYPE_STRING: {
                    std::string value = reflection->GetString(message, field);
                    int wsize = field->name().size();
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    serialized_string->append(field->name().c_str(), field->name().size());
                    wsize = value.size();
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    serialized_string->append(value.c_str(), value.size());
                    break;
                }
                case FieldDescriptor::CPPTYPE_MESSAGE: {
                    std::string value;
                    int wsize = field->name().size();
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    serialized_string->append(field->name().c_str(), field->name().size());
                    const Message& submessage = reflection->GetMessage(message, field);
                    serialize_message(submessage, &value);
                    wsize = value.size();
                    serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
                    serialized_string->append(value.c_str(), value.size());
                    break;
                }
            }
        }
    }
}

void parse_message(const string& serialized_string, Message* message) {
    const Descriptor* descriptor = message->GetDescriptor();
    const Reflection* reflection = message->GetReflection();
    map<string, const FieldDescriptor*> field_map;

    for (int i = 0; i < descriptor->field_count(); ++i) {
        const FieldDescriptor* field = descriptor->field(i);
        field_map[field->name()] = field;
    }

    const FieldDescriptor* field = NULL;
    size_t pos = 0;
    while (pos < serialized_string.size()) {
        int name_size = *(reinterpret_cast<const int*>(serialized_string.substr(pos, sizeof(int)).c_str()));
        pos += sizeof(int);
        string name = serialized_string.substr(pos, name_size);
        pos += name_size;

        int value_size = *(reinterpret_cast<const int*>(serialized_string.substr(pos, sizeof(int)).c_str()));
        pos += sizeof(int);
        string value = serialized_string.substr(pos, value_size);
        pos += value_size;

        map<string, const FieldDescriptor*>::iterator iter =
            field_map.find(name);
        if (iter == field_map.end()) {
            fprintf(stderr, "no field found.\n");
            continue;
        } else {
            field = iter->second;
        }

        assert(!field->is_repeated());
        switch (field->cpp_type()) {
#define CASE_FIELD_TYPE(cpptype, method, valuetype)\
            case FieldDescriptor::CPPTYPE_##cpptype: {\
                reflection->Set##method(\
                        message,\
                        field,\
                        *(reinterpret_cast<const valuetype*>(value.c_str())));\
                cout << field->name() << "\t" << *(reinterpret_cast<const valuetype*>(value.c_str())) << endl;\
                break;\
            }
            CASE_FIELD_TYPE(INT32, Int32, int);
            CASE_FIELD_TYPE(UINT32, UInt32, uint32_t);
            CASE_FIELD_TYPE(FLOAT, Float, float);
            CASE_FIELD_TYPE(DOUBLE, Double, double);
            CASE_FIELD_TYPE(BOOL, Bool, bool);
            CASE_FIELD_TYPE(INT64, Int64, int64_t);
            CASE_FIELD_TYPE(UINT64, UInt64, uint64_t);
#undef CASE_FIELD_TYPE
            case FieldDescriptor::CPPTYPE_ENUM: {
                const EnumValueDescriptor* enum_value_descriptor =
                    field->enum_type()->FindValueByNumber(*(reinterpret_cast<const int*>(value.c_str())));
                reflection->SetEnum(message, field, enum_value_descriptor);
                cout << field->name() << "\t" << *(reinterpret_cast<const int*>(value.c_str())) << endl;
                break;
            }
            case FieldDescriptor::CPPTYPE_STRING: {
                reflection->SetString(message, field, value);
                cout << field->name() << "\t" << value << endl;
                break;
            }
            case FieldDescriptor::CPPTYPE_MESSAGE: {
                Message* submessage = reflection->MutableMessage(message, field);
                parse_message(value, submessage);
                break;
            }
            default: {
                break;
            }
        }
    }
}


void print_field(const Message& message)
{
    const Descriptor* descriptor = message.GetDescriptor();
    const Reflection* reflection = message.GetReflection();

    for (int i = 0; i < descriptor->field_count(); ++i) {
        const FieldDescriptor* field = descriptor->field(i);
        bool has_field = reflection->HasField(message, field);
        assert(!field->is_repeated());
            
        switch (field->cpp_type()) {
#define CASE_FIELD_TYPE(cpptype, method, valuetype)\
            case FieldDescriptor::CPPTYPE_##cpptype:{\
                valuetype value = reflection->Get##method(message, field);\
                if (has_field) {\
                    cout << field->name() << " : " << value << ", type : " << #valuetype << "\n";\
                }  else  {\
                    cout << field->name() << " : " << "None" << ", type : " << #valuetype << "\n";\
                }\
                break;\
            }

            CASE_FIELD_TYPE(INT32, Int32, int);
            CASE_FIELD_TYPE(UINT32, UInt32, uint32_t);
            CASE_FIELD_TYPE(FLOAT, Float, float);
            CASE_FIELD_TYPE(DOUBLE, Double, double);
            CASE_FIELD_TYPE(BOOL, Bool, bool);
            CASE_FIELD_TYPE(INT64, Int64, int64_t);
            CASE_FIELD_TYPE(UINT64, UInt64, uint64_t);
#undef CASE_FIELD_TYPE
            case FieldDescriptor::CPPTYPE_ENUM: {
                int value = reflection->GetEnum(message, field)->number();
                if (has_field)  {
                    cout << field->name() << " : " << value << ", type : " << "enum \n";    
                }
                else  {
                    cout << field->name() << " : " << "None" << ", type : " << "enum \n";
                }
                break;
            }
            case FieldDescriptor::CPPTYPE_STRING: {
                string value = reflection->GetString(message, field);
                if (has_field)  {
                    cout << field->name() << " : " << value << ", type : " << "string \n";  
                }
                else  {
                    cout << field->name() << " : " << "None" << ", type : " << "string \n";
                }
                
                break;
            }
            case FieldDescriptor::CPPTYPE_MESSAGE: {
                const Message& submessage = reflection->GetMessage(message, field);
                print_field(submessage);
                break;
            }
        }
    }
}



int main() 
{
    string str;
	Person person;
	person.set_name("shonm");
	person.set_id(123);
	person.mutable_phone()->set_number("1380000");
	person.mutable_phone()->set_type(Person_PhoneType_WORK);
	serialize_message(person, &str);       //按照自己的方式(反射的欄位)序列化

	Person person2;
	parse_message(str, &person2);          //按照自己的方式反序列化
	printf("\n\n");
	print_field(person);                   //根據反射列印欄位
	
	printf("\n\n");
	print_field(person2);
}

參考: