5
votes

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
1
Do you intend to to use the CodedOutput and CodedInputStream opposed to any System.IO.Stream suggested in the Protocol Buffer Basics for C# in the parsing and serialization section? In the examples of Google Protobuf's Github MemoryStream is used instead of CodedInputStream and deserialization is done by using the Parser.ParseFrom() available for every compiled Protobuf message. - Tornadowarnung
I've never used CodedOutputStream for serializing messages but as it is mentioned in the API for CodedOutputStream it is suggested to use the WriteTag method. This might explain the upcoming error for you having an invalid tag. As both CodedInputStream and CodedOutputStream state "This class is generally used by generated code" I'd think that both aren't generally meant to be used outside of the generated classes. - Tornadowarnung
I can't advise on how exactly to use CodedOutputStream (my experience is with protobuf-net, where you just call Serializer.Serialize(stream, obj), but you can check whether the byte[] data is valid easily here: protogen.marcgravell.com/decode (you can also use protogen.marcgravell.com to quickly generate C# for either protobuf-net or the Google version from your schema, for example: protogen.marcgravell.com/#g54f761a063bbd74e0617b85468a8b06a) - Marc Gravell
@Neele, My problem was essentially 2 things. 1. i didn't rtfm :( 2. I didn't do using Google.Protobuf; so i couldn't understand that you can pass a MemoryStream straight to the Google protobuf. If you add your answer and include this, i will accept it. - Fantastic Mr Fox

1 Answers

4
votes

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);