Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using protobuf CodedInputStream to read from byte[]

Tags:

In the following code i want to use a predefined protobuf message in c#. I found that I was able to write and use the method to take a method that has been created and make a byte[]:

 ContainerMessage containerMessage = new ContainerMessage();
 containerMessage.Type = CommandType.Connect;
 containerMessage.Connect = new Connect();
 containerMessage.Connect.ClientName = "TemporaryClientName";

 byte[] stream = new byte[containerMessage.CalculateSize()];   
 using (Google.Protobuf.CodedOutputStream outstream = new Google.Protobuf.CodedOutputStream(stream))
 {
     containerMessage.WriteTo(outstream);
 }

This works as expected and i can inspect the message and the values are as expected as are the values in the byte[]. But if I try to Deserialize even this simple byte[] that i have just created:

  using (Google.Protobuf.CodedInputStream instream = new Google.Protobuf.CodedInputStream(stream))
  {
      instream.ReadMessage(containerMessage);
  }

It fails with:

An unhandled exception of type 'Google.Protobuf.InvalidProtocolBufferException' occurred in Google.Protobuf.dll

Additional information: Protocol message contained an invalid tag (zero).

Is this way of deserializing from a byte[] correct for protobuf?


The Protobuf Definition is:

message ContainerMessage {
    CommandType type = 1;
    bool commandSuccess = 2;

    oneof message {
        Connect connect = 3;
    }
}

enum CommandType {
    START = 0;
    CONNECT = 2;
}

message Connect {
    string ClientName = 1;
    uint32 PushPullPort = 2;
}

And the CS file is generated with the command line:

protoc.exe -I=%CD% --csharp_out=..\GeneratedCsMessaging\ Connect.proto
like image 438
Fantastic Mr Fox Avatar asked Feb 01 '18 08:02

Fantastic Mr Fox


1 Answers

The CodedOutputStream and CodedInputStream are mainly intended to be used by the compiled proto classes. The API for CodedOutputStream states such and mentions that if you want to have manually-written code calling either of both classes you need to use their WriteTag method before each value.

However, since you want to use the Google Protobuf for serializing and parsing any System.IO.Stream will do the job just like intended. This is very well documented and described in the Parsing and serialization section of the Protocol Buffer Basics for C#. The examples which can be found in Google Protobuf's Github can be quite helpful for getting the hang of Google Protobuf quickly. There you can see that a MemoryStream is used to serialize the object while the Parse.ParseFrom method can be used to parse an object out of the serialized data.

As you've mentioned in the comments to your question using Google.Protobuf; is an essential part to be able to use Google Protobuf's features.

EDIT: A sample usage in your case could look something like this

        byte[] serializedBytes;

        ContainerMessage containerMessage = new ContainerMessage()
        {
            Connect = new Connect()
            {
                ClientName = "TemporaryClientName",
            },
            Type = CommandType.Connect,
        };

        using( MemoryStream stream = new MemoryStream())
        {
            containerMessage.WriteTo(stream);
            serializedBytes = stream.ToArray();
        }

        ContainerMessage parsedCopy = ContainerMessage.Parser.ParseFrom(serializedBytes);
like image 75
Tornadowarnung Avatar answered Oct 03 '22 17:10

Tornadowarnung