protobuf-net: Serializing an empty List

bopa picture bopa · Mar 4, 2010 · Viewed 9.2k times · Source

we have some problems with serializing an empty list. here some code in .NET using CF 2.0

//Generating the protobuf-msg
ProtoBufMessage msg = new ProtoBufMessage();
msg.list = new List<AnotherProtobufMessage>();
// Serializing and sending throw HTTP-POST
MemoryStream stream = new MemoryStream();
Serializer.Serialize(stream, msg);
byte[] bytes = stream.ToArray();
HttpWebRequest request = createRequest();
request.ContentLength = bytes.Length ;

using (Stream httpStream = request.GetRequestStream())
{              
      httpStream.Write(bytes, 0, bytes.Length);
}

we got a exception, when we try to write on the stream (bytes.length out of range). But a type with an empty List should not be 0 bytes, right (type-information?)?

We need this type of sending, because in the Response are the messages from the Server for our client.

Answer

Marc Gravell picture Marc Gravell · Mar 4, 2010

The wire format (defined by google - not inside my control!) only sends data for items. It makes no distinction between an empty list and a null list. So if there is no data to send - yes, the length is 0 (it is a very frugal format ;-p).

Protocol buffers do not include any type metadata on the wire.

Another common gotcha here is that you might assume your list property is automatically instantiated as empty, but it won't be (unless your code does it, perhaps in a field initializer or constructor).

Here's a workable hack:

[ProtoContract]
class SomeType {

    [ProtoMember(1)]
    public List<SomeOtherType> Items {get;set;}

    [DefaultValue(false), ProtoMember(2)]
    private bool IsEmptyList {
        get { return Items != null && Items.Count == 0; }
        set { if(value) {Items = new List<SomeOtherType>();}}
    }
}

Hacky maybe, but it should work. You could also lose the Items "set" if you want and just drop the bool:

    [ProtoMember(1)]
    public List<SomeOtherType> Items {get {return items;}}
    private readonly List<SomeOtherType> items = new List<SomeOtherType>();

    [DefaultValue(false), ProtoMember(2)]
    private bool IsEmptyList {
        get { return items.Count == 0; }
        set { }
    }