3

I am in the process of migrating from BinaryFormatter to Protobuf-net (which so far appears to offer HUGE improvements both in terms of storage size and deserialization time).

A problem I've encountered however is that double?[] arrays do not deserialize in the same form they were serialized. Any values in the array that are null get removed in their entirety - i.e. if I start with an array with 6 elements of [null, null, 1, 2, 3, null], after deserialization I end up with an array of [1, 2, 3]. For my programme, it is essential that I retreive these arrays in exactly the same form they were prior to serialization - as would happen if BinaryFormatter were used.

One solution I have come up with so far is to create two arrays for every one, one of double[] where every element has a value, and one of bool[] which can be used to describe if the original value was null - however this is very inefficient for various reasons.

I could see mentioned in a previous related question that there may be a 'SupportNull' option for a ProtoMember, but I couldn't find any documentation showing clearly how to implement this, and could not work it out myself by playing around.

Any help anyone could offer would be hugely appreciated.

dereknash
  • 155
  • 1
  • 1
  • 6

2 Answers2

2

It actually doesn't support that out of the box, you would have to manipulate the RuntimeTypeModel to explicitly set that it should allow nulls.

RuntimeTypeModel.Default[typeof(YourObjectType)][(tag)].SupportNull = true;

Example:

var nullable = new ObjectWithNullables() { IntArray = new int?[] { null, 1, 2, null } };

// returns 2 elements out of 4
//var resultA = Deserialize<ObjectWithNullables>(Serialize<ObjectWithNullables>(nullable));

RuntimeTypeModel.Default[typeof(ObjectWithNullables)][1].SupportNull = true;

// returns 4 elements out of 4
var resultA = Deserialize<ObjectWithNullables>(Serialize<ObjectWithNullables>(nullable));


    [ProtoContract]
    public class ObjectWithNullables
    {
        [ProtoMember(1)]
        public int?[] IntArray { get; set; }
    }
MichaC
  • 13,104
  • 2
  • 44
  • 56
2
using ProtoBuf;
using ProtoBuf.Meta;
using System;
[ProtoContract]
class Foo
{
    [ProtoMember(1)]
    public double?[] Values { get; set; }
}
static class Program
{
    static void Main()
    {
        // configure the model; SupportNull is not currently available
        // on the attributes, so need to tweak the model a little
        RuntimeTypeModel.Default.Add(typeof(Foo), true)[1].SupportNull = true;

        // invent some data, then clone it (serialize+deserialize)
        var obj = new Foo { Values = new double?[] {1,null, 2.5, null, 3}};
        var clone = Serializer.DeepClone(obj);

        // check we got all the values back
        foreach (var value in clone.Values)
        {
            Console.WriteLine(value);
        }
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    Thanks Marc for excellent sample. But it would be nice to have an attribute for that. Actually, setting that in the static constructor is not always called on deserialization. Then we have to set the "SupportNull" elsewhere than the class where it should be. But doing so goes against the main principle of good programming: hi cohesion and low coupling. – Eric Ouellet Jun 16 '14 at 16:56