c++serializationdeserializationprotocol-buffersbinary-serialization

C++ protobuf how to set values for nested struct


I am learning C++ ProtoBuf.

I have the following struct which I need to serialize:

enum phonetype
{
    DESKPHONE,
    MOBILE,
    WIRELESSPHONE
};

struct phonenumber
{
    int ptype;
    string number;
};

struct address
{
    string addr1;
    string addr2;
};

struct college
{
    string collegename;
    string collegeaddress;
};

struct student
{
    int id;
    string name;
    double age;
    string email;

    struct phonenumber phoneN;
    struct address addr;
    struct college col;
};

I have initialized the struct as follows:

student stud = {123, "Stud_1", 30, "none",
                    {MOBILE, "123456789"}, 
                    {"Boston, US", "None"}, 
                    {"Boston college", "Boston"}};

Now I would like to create a serialized string of this struct for which I have written the following .proto file:

syntax = "proto2";

message studentP
{
  required int32 id = 1;
  required string name = 2;
  required double age = 3;
  optional string email = 4;

  message phonenumberP
  {
    required int32 ptype = 1;
    required string number = 2;
  }
  
  message addressP {
    required string addr1 = 1;
    optional string addr2 = 2;
  }

  message collegeP {
    required string collegename = 1;
    optional string collegeaddress = 2;
  }  
}

In my C++ code, I am setting the proto obj values as follows:

studentP studObj;
studObj.set_name(stud.name);
studObj.set_eid(stud.id);
studObj.set_age(stud.age);

studentP::phonenumberP *phone;
phone->set_ptype(stud.phoneN.ptype);
phone->set_number(stud.phoneN.number);

studentP::addressP *addr;
addr->set_addr1(stud.addr.addr1);
addr->set_addr2(stud.addr.addr2);

studentP::collegeP *coll;
coll->set_collegename(stud.col.collegename);
coll->set_collegeaddress(stud.col.collegeaddress);


string student_str;
studObj.SerializeToString(&student_str);

Above I have separately set the values for the internal structs of class studentP.

How do I set the values for the internal structure of studentP object studObj?

Do I need to call SerializeToString for each internal struct?


Solution

  • As of now, your ProtoBuf schema only contains the definitions for nested messages (phone, address, and college); but, not their respective fields in the Student message type. And, you don't need to have separate structures in your code. They have already been declared and defined in .pb.h and .pb.cc files generated by protoc. Use those. Otherwise, it will be self-defeating to manually maintain those types in code while using a serialization/deserialization library/framework that already does that for you unless you have a pretty good reason to do that.

    The updated ProtoBuf schema will be (observe fields 5, 6, and 7 below):

    studentinfo.proto

    syntax = "proto2";
    
    package test;
    
    message Student
    {
        required int32 id = 1;
        required string name = 2;
        required double age = 3;
        optional string email = 4;
    
        enum PhoneType
        {
            DESK = 1;
            MOBILE = 2;
            WIRELESS = 3;
        };
    
        message Phone
        {
            required PhoneType type = 1;
            required string number = 2;
        }
    
        message Address {
            required string address1 = 1;
            optional string address2 = 2;
        }
    
        message College {
            required string name = 1;
            optional string address = 2;
        }
    
        required Phone phone = 5;
        required Address address = 6;
        optional College college = 7;
    }
    

    Once you have the required schema and generated files in place, you can go on creating and populating types, and then serialize the messages to send over the wire and deserialize on the other end.

    Here's a complete working example:

    main.cpp

    #include <iostream>
    #include <string>
    #include "studentinfo.pb.h"
    
    int main()
    {
        using namespace test;
    
        // Serialization
    
        Student s;
        s.set_name("Test");
        s.set_id(123);
        s.set_age(24);
    
        s.mutable_phone()->set_type(Student_PhoneType_DESK);
        s.mutable_phone()->set_number("+00 123 1234567");
    
        s.mutable_address()->set_address1("House # 1, Street # 1");
        s.mutable_address()->set_address2("House # 2, Street # 2");
    
        s.mutable_college()->set_name("XYZ College");
        s.mutable_college()->set_address("College Address Here");
    
        std::cout << "Serialization:\n\n" << s.DebugString() << "\n\n";
        //s.PrintDebugString();
    
        std::string serialized;
        if ( !s.SerializeToString( &serialized ) )
        {
            std::cerr << "ERROR: Unable to serialize!\n";
            return -1;
        }
    
        // Deserialization
    
        Student deserialized;
        if ( !deserialized.ParseFromString( serialized ) )
        {
            std::cerr << "ERROR: Unable to deserialize!\n";
            return -1;
        }
    
        std::cout << "Deserialization:\n\n";
        deserialized.PrintDebugString();
    
        // deserialized.name();
        // deserialized.id();
        // ...
        // deserialized.phone().type();
        // deserialized.phone().number()
        // ...
    
        return 0;
    }
    

    Output:

    Serialization:
    
    id: 123
    name: "Test"
    age: 24
    phone {
      type: DESK
      number: "+00 123 1234567"
    }
    address {
      address1: "House # 1, Street # 1"
      address2: "House # 2, Street # 2"
    }
    college {
      name: "XYZ College"
      address: "College Address Here"
    }
    
    
    Deserialization:
    
    id: 123
    name: "Test"
    age: 24
    phone {
      type: DESK
      number: "+00 123 1234567"
    }
    address {
      address1: "House # 1, Street # 1"
      address2: "House # 2, Street # 2"
    }
    college {
      name: "XYZ College"
      address: "College Address Here"
    }
    

    Take a look at .pb.h file for the mutators and accessors that you might need to manipulate your messages.