有用的传送门:https://www.cnblogs.com/shitouer/archive/2013/04/12/protocol-buffers-language-guide.html
Protobuf(Protocol Buffers)是由 Google 开发的一种轻量级、高效的数据交换格式,它被用于结构化数据的序列化、反序列化和传输。相比于 XML 和 JSON 等文本格式,Protobuf 具有更小的数据体积、更快的解析速度和更强的可扩展性。Protobuf 的核心思想是使用协议(Protocol)来定义数据的结构和编码方式。使用 Protobuf,可以先定义数据的结构和各字段的类型、字段等信息,然后使用 Protobuf 提供的编译器生成对应的代码,用于序列化和反序列化数据。由于 Protobuf 是基于二进制编码的,因此可以在数据传输和存储中实现更高效的数据交换,同时也可以跨语言使用。
将已有对象的信息写入protobuf,根据protobuf中的信息创建对象,这两个过程类似于序列化和反序列化。在这里介绍proto有关的常见用法,可能不全面但是好用!
定义proto结构
使用 Protobuf 的语言定义文件(.proto)可以定义要传输的信息的数据结构,可以包括各个字段的名称、类型等信息。同时也可以相互嵌套组合,构造出更加复杂的消息结构。消息名称以大写字母开头,每个字段定义包含一个字段类型、字段名称、字段编号(可选)、字段选项(可选)。
message的没一个字段,都要用如下的三个修饰符(modifier)来声明:
- required:必须赋值,不能为空,否则该条message会被认为是“uninitialized”。build一个 “uninitialized” message会抛出一个RuntimeException异常,解析一条“uninitialized” message会抛出一条IOException异常。
- optional:字段可以赋值,也可以不赋值。假如没有赋值的话,会被赋上默认值。对于简单类型,默认值可以自己设定,当获取没有显式 设置值的optional字段的值时,就会返回该字段的默认值。
- repeated:该字段可以重复任意次数,包括0次。重复数据的顺序将会保存在protbuf中。
e.g. 一个简单的proto消息结构
syntax = "proto2";
message FamilyPb{
required string address = 1; [default = "Munich"]
repeated PersonPb members;
}
message PersonPb{
required string name = 1;
required int32 age = 2 【default = 10】;
optional string hobby = 3;
}
message PetPb{
required string name = 1;
required string breed = 2;
}
在进行对于proto的读写之前,我们先根据上面的proto结构建立相关的类
struct Person{
string name;
int age;
std::string hobby;
PersonPb ToProto(){
PersonPb person_pb;
person_pb->set_name(name);
person_pb->set_age(age);
person_pb->set_hobby(hobby);
}
};
struct Pet{
std::string name;
std::string breed;
};
class Family{
public:
FamilyPb ToProto();
// 通过set_xxx(), add_xxx()用来写入private 成员变量的函数
inline void add_member(const Person& person){
family_members_.emplace_back(person);
}
inline void set_pet(const Pet& pet){
pet_ = pet;
}
inline void set_address(const std::string address){
address_ = address;
}
// 通过xxx()用来读取private 成员变量的函数
const std::vector<Person> family_members(){
return family_members_;
}
const Pet pet(){
return pet_;
}
const std::string address(){
return address_;
}
private:
std::string address_;
std::vector<Person> family_members_;
Pet pet_;
};
//开始对上述类的对象进行初始化
Peroson mom{
.name = "Mom",
.age = 30,
.hobby = "reading"
};
Peroson dad{
.name = "Dad",
.age = 40,
.hobby = "swimming"
};
Peroson son{
.name = "Son",
.age = 5,
.hobby = "hiking"
};
Peroson daughter{
.name = "Daughter",
.age = 1,
.hobby = "sleeping"
};
Pet pet{
.name = "Garfie",
.breed = Cat
};
Family family;
family.add_member(mom);
family.add_member(dad);
family.add_member(son);
family.add_member(daughter);
family.set_pet(pet);
family.set_address("FreissingerLandstrasse");
给protobuf消息赋值
将上面的Family类的对象family中的信息赋值给FamilyPb
-
对于required或者optional字段,可以直接使用set方法赋值。
-
对于嵌套结构,可以先获取子结构的指针,mutable_xxx()返回的是嵌套的子结构的指针,然后可以使用set方法给子结构赋值。
-
对于repeated字段,可以先获取子结构的指针,add_xxx()返回的是repeated子结构的指针,然后可以使用set方法给子结构赋值。也可以直接使用实现定义好的ToProto()方法赋值!实际应用中protobuf可能会嵌套很多层,所以通常会按照嵌套关系,从上到下的调用每一层结构的ToProto()。
FamilyPb Family::ToProto(){
FamilyPb family_pb;
// 对于required或者optional字段,可以直接使用set方法赋值
family_pb.set_address(family.address());
// 对于嵌套结构,可以先获取子结构的指针,mutable_xxx()返回的是嵌套的子结构的指针
// 然后可以使用set方法给子结构赋值
family_pb.mutable_pet()->set_name(family.pet().name);
family_pb.mutable_pet()->set_breed(family.pet().breed);
// 对于repeated字段,可以先获取子结构的指针,add_xxx()返回的是repeated子结构的指针
// 然后可以使用set方法给子结构赋值
for(const auto& member: family.family_members()){
PersonPb* person_pb = family_pb.add_members();
person_pb ->set_name(member.name);
person_pb ->set_age(member.age);
person_pb ->set_hobby(member.hobby);
//也可以直接使用ToProto()方法赋值
//*family_pb.add_members() = member.ToProto();
}
return family_pb;
}
从protobuf消息中读取信息并初始化各个类
- 对于required或者optional字段,e.g. familypb.address()直接返回的是std::string
-
对于嵌套结构,e.g. familypb.pet()返回的是PetPb。
-
对于repeated字段,可以用xxx_size()来查看repeated字段members包含有多少个PersonPb,然后用members(i)来调用members中的第i个FamilyPb。
Family read_from_protobuf(FamilyPb familypb){
Family family;
// 对于required或者optional字段,e.g. familypb.address()直接返回的是std::string
family.set_address(familypb.address());
// 对于嵌套结构,familypb.pet()返回的是PetPb
Pet pet;
pet.name = familypb.pet().name();
pet.breed = familypb.pet().breed();
family.set_pet(familypb.pet());
// 对于repeated字段,可以用members(i)来调用第i个FamilyPb
for (int i = 0; i < familypb.members_size(); i++){
Person person;
person.name = familypb.members(i).name();
person.age = familypb.members(i).age();
person.hobby = familypb.members(i).hobby();
}
return family;
}