BaltoStar
BaltoStar

Reputation: 8977

protobuf-net : list with null elements

public MyType {

  [ProtoMember(1)]
  public int Index;

  [ProtoMember(2)]
  public string Name;

  public MyType() {
  }

  public MyType(int index, string name) {
    Index = index;
    Name = name;
  }
}

var element0 = new MyType(0,"element index 0");
var element1 = null;
var element2 = new MyType(2,"element index 2");
var list = new List<MyType> {element0, element1, element2}

Since element index 1 is null , protobuf-net serializer will omit that element from the serialized bytes.

On the other side of the wire, something very interesting happens :

protobuf-net deserializer reads the bytes, learns that 3 elements are expected, but only finds bytes for elements index 0 and 2. So it creates element index 1 as an empty instance of MyType.

So the deserialized list is equivalent to :

var element0 = new MyType(0,"element index 0");   
var element1 = new MyType(0,null); 
var element2 = new MyType(2,"element index 2");
var list = new List<MyType> {element0, element1, element2}

But this is not what is expected. Null is not the same thing as an empty object, and the presence of an element with duplicate Index=0 and Name=null can have serious adverse consequences for the processing code - which rightfully should not be aware of nor concerned with serdes details.

Is there a workaround ?

Upvotes: 4

Views: 2078

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062780

Basically, no, there's no workaround for this and it is not a supported scenario in protobuf-net. The underlying protocol buffers format (which protobuf-net implements) has no concept of null; there is no way for me to represent a null instance inside the regular format. A list of classes is simply a repeated chunk of length-prefixed nodes, so (in a dense binary format, not text):

[field 1, length-prefixed] [length prefix] [payload for element index 0]
[field 1, length-prefixed] [length prefix] [payload for element index 1]
[field 1, length-prefixed] [length prefix] [payload for element index 2]
... etc

The only two options I have for nulls in a list are:

  • ignore them
  • treat them as zero-length

Protobuf-net currently uses the second approach, but: a zero-length class is essentially new MyType(); zero-length is perfectly valid and well-defined in protocol buffers.

Unlike some other formats, there isn't really anywhere I can add extra metadata to say "I'm a null" (@xsi:nil in xml, for example).

It might be possible for me to hack this into the "preserve references" code, but I've checked, and support for nulls isn't there currently - I accept that this is a bit of an oversight, but I would also emphasize that the "preserve references" code is not standard protobuf, and any other libraries (java, golang, etc) may have difficulty consuming that configuration; I would only advocate that if you know you're only talking protobuf-net to protobuf-net. And of course, the null support doesn't currently exist, and would need hacking in :)

Upvotes: 2

Related Questions