Search code examples
c#protocol-buffersprotobuf-net

How to decode raw proto byte array


Using HttpClient I can get the byte array data where it's a protocol buffer. How to decode it using protobuf-net without knowing the type? This stackoverflow answer doesn't work anymore in the latest version where I can't instantiate the ProtoReader.


Solution

  • You can still instantiate the reader API in v3. The only difference is that you should useCreate rather than new, and if at all possible: use the new ProtoReader.State API instead. Other than that, it should stay largely the same. Plus you don't need a MemoryStream any more (you can use raw buffers such as a byte[] directly).

    Here's a translated version using v3 and the State API:

    static void Main()
    {
        byte[] arr = // ...
        var reader = ProtoReader.State.Create(arr, null, null);
        try
        {
            WriteTree(ref reader);
        }
        finally
        {   // the rules on when you can "using" a ref-type
            // are ... complicated
            reader.Dispose();
        }
    }
    static void WriteTree(ref ProtoReader.State reader)
    {
        while (reader.ReadFieldHeader() > 0)
        {
            Console.WriteLine(reader.FieldNumber);
            Console.WriteLine(reader.WireType);
            switch (reader.WireType)
            {
                case WireType.Varint:
                    // warning: this appear to be wrong if the 
                    // value was written signed ("zigzag") - to
                    // read zigzag, add: pr.Hint(WireType.SignedVariant);
                    Console.WriteLine(reader.ReadInt64());
                    break;
                case WireType.String:
                    // note: "string" here just means "some bytes"; could
                    // be UTF-8, could be a BLOB, could be a "packed array",
                    // or could be sub-object(s); showing UTF-8 for simplicity
                    Console.WriteLine(reader.ReadString());
                    break;
                case WireType.Fixed32:
                    // could be an integer, but probably floating point
                    Console.WriteLine(reader.ReadSingle());
                    break;
                case WireType.Fixed64:
                    // could be an integer, but probably floating point
                    Console.WriteLine(reader.ReadDouble());
                    break;
                case WireType.StartGroup:
                    // one of 2 sub-object formats
                    var tok = reader.StartSubItem();
                    WriteTree(ref reader);
                    reader.EndSubItem(tok);
                    break;
                default:
                    reader.SkipField();
                    break;
            }
        }
    }
    

    (the non-State API is still usable, but this is preferred)