How to add dynamically a list of known type to a Protobuf-net formatter?
In a project, I'm currently using a DataContractSerializer
to serialize my data. And I'd like to use Protobuf
instead for performance reason.
I use a serializer to store blobs inside a database, and I want to keep the data versioning features of WCF to handle more easily data contract modifications once the project is released in the real world.
The data contracts of these blobs are not known at compile time. (A list of "extension" is specified in the config file and so the extensions register DataContracts
they know at runtime)
However it seems that the only way to tell protobuf about known type is with the [ProtoInclude]
attribute.
To be more specific, here is an example of what I want to serialize.
[DataContract]
public class Mydata
{
[DataMember(Order = 1)]
public int Saved
{
get;
set;
}
}
[DataContract]
public class Container
{
[DataMember(Order = 1)]
public object Data
{
get;
set;
}
}
With DataContractSerializer
here how I do :
[TestMethod]
public void SerializeWithDataContractSerializer()
{
var container = new Container()
{
Data = new Mydata()
{
Saved = 1
}
};
DataContractSerializer serializer = new DataContractSerializer(typeof(Container), new[] { typeof(Mydata) });
var ms = new MemoryStream();
serializer.WriteObject(ms, container);
ms.Position = 0;
var containerSerialized = (Container)serializer.ReadObject(ms);
Assert.AreEqual(((Mydata)container.Data).Saved, ((Mydata)conta开发者_运维问答inerSerialized.Data).Saved);
}
With protobuf
here how I wanted to do (but not possible Serializer.CreateFormatter<T>()
does not take a knownTypes
parameter) :
[TestMethod]
public void SerializeWithProtoBuf()
{
var container = new Container()
{
Data = new Mydata()
{
Saved = 1
}
};
var formatter = Serializer.CreateFormatter<Container>(new[] { typeof(Mydata) });
var ms = new MemoryStream();
formatter.Serialize(ms, container);
ms.Position = 0;
var containerSerialized = (Container)formatter.Deserialize(ms);
Assert.AreEqual(((Mydata)container.Data).Saved, ((Mydata)containerSerialized.Data).Saved);
}
Any solution ?
Since you are serialising a root object, in v1 (the fully released dll) you can do this using the Serializer.NonGeneric.SerializeWithLengthPrefix and DeserializeWithLengthPrefix API, in particular using a different field-number (tag) per-type when serialising, and using the deserialize overload that accepts a type-resolver to translate a irks-number back into a Type. I'm not at a PC currently but I can add an example later of you need.
In "v2" there are additional options here:
- you can mark a property (etc) as DynamicTypr=true, which stores additional type metadata making this work even for object properties, but making it a bit more tied to your model
- you can add known sub-types at runtime via the flexible type-model; however generally this wouldn't be from
object
, and I am still exploring possible side-effects of this (it has been requested as a feature previously)
精彩评论