Search code examples
c#protocol-buffersprotobuf-net

Invalid wire-type error in protobuf.net on a new MemoryStream


I'm trying to parse a protobuf message received from an external service. I know that this data is a valid protobuf message, and I know I'm not accidentally mangling it before I send it to the deserializer. Here's my code:

public static T DeserializeFromBytes<T>(byte[] encoded)
{
    MemoryStream stream = new MemoryStream(encoded);
    stream.SetLength(encoded.Length);
    stream.Capacity = encoded.Length;
    T decoded = (T)Serializer.Deserialize<T>(stream);
    stream.Close();
    return decoded;
}

and I'm calling this method like this:

MercuryReply header = ProtobufUtils.DeserializeFromBytes<MercuryReply>(Convert.FromBase64String(metadata[0]));

metadata[0] is a base64 string that includes a protobuf message. A sample looks something like this:

CjRobTovL21ldGFkYXRhL2FsYnVtL2M1MzU3MTA0M2U4ODQ3YjRhODc1YzVlNmZiNmNiZTdmEhp2bmQuc3BvdGlmeS9tZXRhZGF0YS1hbGJ1bSCQAzIYCgpNRC1WZXJzaW9uEgoxMzcwMzc5NTA1Mg8KBk1DLVRUTBIFNjk2MDQyGQoPTUMtQ2FjaGUtUG9saWN5EgZwdWJsaWMyDwoHTUMtRVRhZxIELD8q+Q==

Now, when I run the program, I get an Invalid wire-type error when deserializing. I can't see any reason why this would be happening. Is there something obvious here that I missed?


Solution

  • That usually means either:

    • the binary is not valid (people make a mess of binary all the time; your use of base-64 is perfectly fine, though, if it needs to be stored/transferred as a string)
    • there is a mismatch in the data (for example, field 2 is varint in the data, but is a string in the type)

    Based on you binary, I've pieced together that MercuryReply might look something like:

    [ProtoContract]
    public class MercuryReply
    {
        [ProtoMember(1)]
        public string Location { get; set; }
        [ProtoMember(2)]
        public string ContentType { get; set; }
        [ProtoMember(4)]
        public int ResponseStatus { get; set; }
        [ProtoMember(6)]
        public Dictionary<string, string> Headers { get { return headers; } }
        private readonly Dictionary<string, string> headers
            = new Dictionary<string, string>();
    }
    

    (the names here are pure guesswork based on what the data looks like; protobuf does not transmit names - only the numeric keys, i.e. 1/2/4/6 in this example)

    which works fine when tested with:

    static void Main()
    {
        var encoded = Convert.FromBase64String("CjRobTovL21ldGFkYXRhL2FsYnVtL2M1MzU3MTA0M2U4ODQ3YjRhODc1YzVlNmZiNmNiZTdmEhp2bmQuc3BvdGlmeS9tZXRhZGF0YS1hbGJ1bSCQAzIYCgpNRC1WZXJzaW9uEgoxMzcwMzc5NTA1Mg8KBk1DLVRUTBIFNjk2MDQyGQoPTUMtQ2FjaGUtUG9saWN5EgZwdWJsaWMyDwoHTUMtRVRhZxIELD8q+Q==");
    
        MercuryReply header = ProtobufUtils.DeserializeFromBytes<MercuryReply>(encoded);
    
    }
    

    (using your DeserializeFromBytes method)

    So: if that isn't working for you, the important two questions are:

    • is that the actual base-64 from your real code at runtime? or is that just what you think it should be in theory?
    • what does your MercuryReply class look like?