关于protobuf的碎碎念

有用的传送门: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;
}

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值