1

Given the following code:

class Data
{
    public List<int> numbers = new List<int>();
}

Where MetaType.UseConstructor is set to false (in order or avoid creating an empty constructor), I'd like to find a way to actually store the empty container. I'm aware that protobuf-net is going to correctly serialize/deserialize if the list has at least one item, but if the list is empty, I got a null reference when deserializing. Of course its resolved by using constructors, but while integrating protobuf-net to a large codebase, having to create an empty constructor for each class is super hard and creates ambiguities for constructors with all-optional parameters.

Can protobuf-net correctly store a valid reference to an empty container?

  • 1
    What about using a property to lazy-initialize the list? (In the getter, checking to see if it's null and constructing the list then.) – cdhowie Aug 14 '14 at 14:29
  • Can you clarify "having to create an empty constructor for each class is super hard" please? You know, there is [always a constructor](http://stackoverflow.com/q/9274573/1997232). – Sinatr Aug 14 '14 at 14:39
  • For example, if you have 200+ classes, and most of them are only constructed with parameters (intended), you are obligated to write a new parameterless constructor for each one, and also, you may silently introduce the side effect of allowing to create an object without the proper parameters (which may be an invalid object). – Federico Oro Vojacek Aug 14 '14 at 14:46
  • @cdhowie I'm trying to get to a solution where there is minimal change in the codebase and minimal requirements so programmers cannot infringe the serialization model easily. – Federico Oro Vojacek Aug 14 '14 at 14:48

1 Answers1

1

The protobuf wire format has no concept of containers; just elements. So in terms of data, no there is nothing whatsover from which to do this.

From the discussion on constructors, I'm guessing that you have enabled constructor-skipping, for example:

[ProtoContract(SkipConstructor=true)]

In that case, indeed: field-initializers will not be invoked, and protobuf-net will not initialize fields that it doesn't see in the data. So in the case of an empty list, yes it will result in a null. That shouldn't actually cause any errors during deserialization, since protobuf-net will initialize the list if it has a need to touch it - but yes, I can understand that some other code could touch the field.

Adding a parameterless constructor and removing the SkipConstructor would allow the existing field-initializers to run; for example:

[Obsolete("For serialization purposes only")]
private void Data() {}

Another option would be to use a deserialization callback, i.e.

[ProtoBeforeDeserialize]
private void Init() {
    numbers = new List<int>();
}

although this adds work (we now need to remember all the fields with field-initializers)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Is it feasible to add to protobuf-net support for empty generic containers? Maybe by forcing the serialization of a special 'empty' object that will be restored to a new empty reference ? – Federico Oro Vojacek Aug 14 '14 at 20:53
  • If I serialize an empty container and there are no errors generated from it, I expect the deserialization process to provide the same empty container. What about multiple fields referencing the same empty container? Its not that atypical if you think about it. Its just a reference tracked object. – Federico Oro Vojacek Aug 14 '14 at 21:08
  • @Federico which sounds easy until you realize that the protobuf wire format had no concept of references. – Marc Gravell Aug 14 '14 at 21:57