Google Protocol Buffer 的常規用法需要使用 protoc
將 .proto
編譯成 .pb.h
和 .pb.cc
,這樣做效率非常高,但是耦合性也很高。在某些追求通用性而不追求效能的場景下,需要使用 .proto
直接操作 protobuf 資料。
本例使用的 .proto
檔案來自 https://developers.google.com/protocol-buffers/docs/cpptutorial ,但是把它拆成了兩個 .proto
檔案
// ./proto/person.proto
syntax = "proto2";
package tutorial;
message Person {
optional string name = 1;
optional int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
optional string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phones = 4;
}
// ./proto/person.proto
syntax = "proto2";
package tutorial;
import "person.proto";
message AddressBook {
repeated Person people = 1;
}=
示例程式碼
#include <iostream>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/util/json_util.h>
using namespace google::protobuf;
/*
構造 Importer 必須指定 error_collector 用於處理錯誤資訊
AddError 是純虛擬函式,必須 override
*/
class MyMultiFileErrorCollector : public compiler::MultiFileErrorCollector
{
virtual void AddError(const std::string& filename, int line, int column, const std::string& message) override
{
std::cout << "file: " << filename << ", line: " << line << ", col: " << column<< ", message: " << message << std::endl;
}
};
int main()
{
/*
構造 DiskSourceTree,並新增虛擬路徑。protobuf 使用 Importor 匯入 .proto 檔案時,會虛擬路徑進行查詢
在本例中,匯入 addressbook.proto 時會使用 ./proto/addressbook.proto
*/
compiler::DiskSourceTree disk_source_tree;
disk_source_tree.MapPath("", "proto");
MyMultiFileErrorCollector error_collector;
/*
匯入 addressbook.proto 時,會自動匯入所有依賴的 .proto 檔案
在本例中,person.proto 也會被自動匯入
*/
compiler::Importer importer(&disk_source_tree, &error_collector);
const FileDescriptor* file_descriptor = importer.Import("addressbook.proto");
if (!file_descriptor) {
exit(-1);
}
// 把 addressbook.proto 和 person.proto 都列印出來
std::cout << "====== all .proto files ======" << std::endl;
std::cout << file_descriptor->DebugString() << std::endl;
for (int i = 0; i < file_descriptor->dependency_count(); ++i) {
std::cout << file_descriptor->dependency(i)->DebugString() << std::endl;
}
// 構造 DynamicMessageFactory 用於動態構造 Message
DynamicMessageFactory message_factory(importer.pool()->generated_pool());
/*
查詢 Person 的 Descriptor
不能使用 file_descriptor 查詢,它只包含 addresssbook.proto ,只能找到 AddressBook,而 DescriptorPool 包含了所有資料
在使用 DescriptorPool 查詢時需要使用全名,如:tutorial.Person
在使用 FileDescritor 查詢需要使用頂級名字,如 AddressBook,而不是 tutorial.AddressBook
*/
const Descriptor* person_descriptor = importer.pool()->FindMessageTypeByName("tutorial.Person");
/*
使用工廠建立預設 Message,然後構造一個可以用來修改的 Message
這個 Message 的生命週期由 New 呼叫者管理
*/
const Message* default_person = message_factory.GetPrototype(person_descriptor);
Message* person = default_person->New();
// 使用 Reflection 修改 Message 的資料
const Reflection* reflection = person->GetReflection();
reflection->SetString(person, person_descriptor->FindFieldByName("name"), "abc");
reflection->SetInt32(person, person_descriptor->FindFieldByName("id"), 123456);
reflection->SetString(person, person_descriptor->FindFieldByName("email"), "abc@163.com");
// 把動態設定的 Message 的資料以 JSON 格式輸出
util::JsonPrintOptions json_options;
json_options.add_whitespace = true;
json_options.preserve_proto_field_names = true;
string output;
util::MessageToJsonString(*person, &output, json_options);
std::cout << "====== Person data ======" << std::endl;
std::cout << output;
// 析構 person
delete person;
}
輸出
====== all .proto files ======
syntax = "proto2";
import "person.proto";
package tutorial;
message AddressBook {
repeated .tutorial.Person people = 1;
}
syntax = "proto2";
package tutorial;
message Person {
message PhoneNumber {
optional string number = 1;
optional .tutorial.Person.PhoneType type = 2 [default = HOME];
}
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
optional string name = 1;
optional int32 id = 2;
optional string email = 3;
repeated .tutorial.Person.PhoneNumber phones = 4;
}
====== Person data ======
{
"name": "abc",
"id": 123456,
"email": "abc@163.com"
}
https://developers.google.com/protocol-buffers/docs/reference/cpp