Why my Protobuf class can parse a string serialized by an other Protobuf class

折月煮酒 提交于 2019-12-24 23:01:04

问题


I tried to know what package I got in my tcp socket, so I use protobuf. But when I SerializeToString my first protobuf class, the ParseFromString method of an other protobuf class returns true.

The two classes are differents

Here are my .proto

syntax = "proto3";

package protobuf;
message Message
{
    string content = 1;
}
message Player
{
    int32 id = 1;
    string name = 2;
}

Here is my c++ code

auto messageProto = new protobuf::Message;
messageProto->set_content("Hello");

std::string data;
messageProto->SerializeToString(&data);

protobuf::Player player;
if (player.ParseFromString(data))
{
    qDebug() << "parse player";
}

protobuf::Message message2;
if (message2.ParseFromString(data))
{
    qDebug() << "parse message";
}

Output :

parse player
parse message

Why ?


回答1:


My recommended solution to the problem of multiple different payloads:

syntax = "proto3";

package protobuf;
message RenameMe // the outer payload wrapper
{
  oneof payload
  {
    Foo foo = 1;
    Bar bar = 2;
  }
}
message Foo // one type of content
{
    string content = 1;
}
message Bar // another type of content
{
    int32 id = 1;
    string name = 2;
}

Now you can just deserialize everything as a RenameMe (naming is hard!) and check the payload discriminated union enum to see how you should interpret the data. Then just access foo or bar respectively.

This approach is clear, obvious, and readily and effectively extensible into additional message types. The testing can be implemented with switch in many programming languages. This style also works well with polymorphism in some environments - for example, in C# with protobuf-net that could be serialized/deserialized with:

[ProtoContract, ProtoInclude(1, typeof(Foo)), ProtoInclude(2, typeof(Bar))]
abstract class RenameMe {}

[ProtoContract]
class Foo : RenameMe {
  [ProtoMember(1)] public string Content {get;set;}
}

[ProtoContract]
class Bar : RenameMe {
  [ProtoMember(1)] public int Id {get;set;}
  [ProtoMember(2)] public string Name {get;set;}
}



回答2:


EDIT : So, now, is it the best approach ?

syntax = "proto3";

message Header
{
    oneof payload
    {
        Message message = 1;
        Player player = 2;
    }
}
message Message
{
    string content = 1;
}
message Player
{
    int32 id = 1;
    string name = 2;
}

When I write :

void SocketManager::sendData(Player& player)
{
    Header header;
    header.set_allocated_player(&player);
    write(header);
}

void SocketManager::sendData(Message& message)
{
    Header header;
    header.set_allocated_message(&message);
    write(header);
}

// etc... for each kind of message

When I read :

void read(const std::string& data)
{
    protobuf::Header header;
    header.ParseFromString(data);

    switch (header.payload_case())
    {
    case protobuf::Header::kMessage:
        emit messageProtoReceived(header.message());
        break;
    case protobuf::Header::kPlayer:
        emit playerProtoReceived(header.player());
        break;
    case protobuf::Header::PAYLOAD_NOT_SET:
        qDebug() << "Error, the payload isn't set, please create a header with a payload";
        break;
    }
}


来源:https://stackoverflow.com/questions/57287462/why-my-protobuf-class-can-parse-a-string-serialized-by-an-other-protobuf-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!