10

Came across protobuf-net, awesome! I have a question regarding serialization of empty lists.

I start by declaring the object I want to serialize:

[ProtoContract]
class TestClass
{
    [ProtoMember(1)]
    List<int> _listOfInts = new List<int>();

    public TestClass() { }

    public List<int> ListOfInts
    {
        get { return _listOfInts; }
        set { _listOfInts = value; }
    }
}

If _listOfInts is empty (but not null) when I deserialse this object is will always be null. This makes sense looking at the protobuf convention and I currently work around this by adding the following method:

[ProtoAfterDeserialization]
private void OnDeserialize()
{
    if (_listOfInts == null)
        _listOfInts = new List<int>();
}

My question is whether I can achieve this same functionality in a more concise fashion, possibly with an additional attirbute which will initialise null/empty objects as empty instead of null?

CanCan
  • 119
  • 6

2 Answers2

7

There's a fundamental issue here in terms of how protobuf encodes data: the list itself does not appear in the data - just the elements. Because of this, there is simply nowhere obvious to store information about the list. It can be spoofed by sending a Boolean using conditional serialization, but frankly that is a bit hacky and ugly - and adds complexity. Personally, I strongly advise abstracting away from lists that could ever be null. For example:

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

Or

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

And note that this advice isn't just about serialization: it is about avoiding arbitrary null-reference-exceptions. People don't usually expect sub-collections to be null.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Is there any reason you don't initialise the collection on the private items field? I can see that if you haven't accessed the getter of the Items property you're not hanging on to an empty collection reference, but you're paying the price of a null check every time. Is it just situational? – Shaun Nov 26 '13 at 16:40
5

If you are trying to protect against a null list you could try lazy loading in the property getter.

public List<int> ListOfInts
{
    get { return _listOfInts ?? (_listOfInts = new List<int>()); }
    set { _listOfInts = value; }
}

This way you can just allow the serializer to return null.

Jras
  • 518
  • 3
  • 12
  • This is a possibility I had not considered, would up vote you if I could. I'm still primarily interested to know if there is an attribute that would do this for me though. – CanCan May 05 '13 at 21:34
  • Have you tried the [DefaultValue] attribute? Not sure if you could initialize lists but worth a try. – Jras May 05 '13 at 21:41
  • @Jras I did consider adding such, but every time I've looked at it, it turns out to actually make people's code worse, not better – Marc Gravell May 06 '13 at 00:06
  • @Marc Gravell I agree, If the list should never be null it should be handled in the Property and not in the serializer. – Jras May 06 '13 at 14:56