Firstly, I must emphasise that everything here relates to protobuf-net, not to the more general "protobuf" implementations.
Yes, that is doable, via a combination of configuration at runtime, and a "surrogate".
Firstly: what is a "surrogate" here: well, the layout of Param, with "object" etc, is not very protobuf-net friendly, so protobuf-net allows you to write a separate, more-suitable-for-serialization type, and flip between them at will. To do this, protobuf-net uses the .NET conversion operators (implicit or explicit), for example:
[ProtoContract]
public class ParamSurrogate
{
[ProtoMember(1)]
public FloatData FloatData { get; set; }
// TODO: other types here
public static implicit operator ParamSurrogate(Param value)
{
if (value == null) return null;
var surrogate = new ParamSurrogate();
if(value.Item != null)
{
surrogate.FloatData = value.Item as FloatData; // will be null if not this
// TODO: other types here
}
return surrogate;
}
public static implicit operator Param(ParamSurrogate value)
{
if (value == null) return null;
var param = new Param();
if (value.FloatData != null) param.Item = value.FloatData;
// TODO: other types here
return param;
}
}
Note that for convenience I'm using attributes to describe the protobuf-net options for the surrogate, but that could also be avoided if necessary. Note that we also need to tell protobuf-net to *use* that surrogate whenever it sees a Param, which can be done via:
// configure model (do once at app startup)
var model = RuntimeTypeModel.Default;
model.Add(typeof(Param), false).SetSurrogate(typeof(ParamSurrogate));
Now, the FloatData is not actually too problematic - simple values are arrays are fine. All we need to do is tell it about the members we want to serialize, and which keys to use. This is as simple as (continuing that initial configuration):
model.Add(typeof (FloatData), false).Add("Ranges", "AdjustValue", "Values");
//TODO: other types here
which gives the 3 members the keys 1, 2, 3 respectively.
We can test this:
// test data
var param = new Param
{
Item = new FloatData
{
AdjustValue = 123.45F,
Ranges = new float[] { 1.0F, 2.4F },
Values = new float[] { 7.21F, 19.2F }
}
};
// note the fallowing is the same as Serializer.DeepClone, since
// model === RuntimeTypeModel.Default
var clone = (Param) model.DeepClone(param);
Assert.AreNotSame(clone, param, "Different instance");
Assert.IsInstanceOfType(typeof(FloatData), clone.Item, "Data type");
var data = (FloatData) clone.Item;
Assert.AreEqual(123.45F, data.AdjustValue);
Assert.AreEqual(2, data.Ranges.Length);
Assert.AreEqual(1.0F, data.Ranges[0]);
Assert.AreEqual(2.4F, data.Ranges[1]);
Assert.AreEqual(2, data.Values.Length);
Assert.AreEqual(7.21F, data.Values[0]);
Assert.AreEqual(19.2F, data.Values[1]);
Which all works fine, demonstrating that we have serialized and deserialized the data correctly.
Marc