Comment intégrer des informations de type à des fins de dé / sérialisation à l’aide de protobuf-net

J’aimerais pouvoir sérialiser des instances concrètes d’IMessage de manière à ce que les informations de type soient conservées / incorporées (comme dans Json.NET, par exemple), afin que, lors de la désérialisation, ces informations de type puissent être matérialisées. cas concrets. Je suis bien conscient que les méthodes de dé / sérialisation ci-dessous ne fonctionnent pas. Tout conseil serait apprécié sur la façon de les changer afin qu’ils fonctionnent.

public interface IMessage {} public interface IEvent : IMessage {} [ProtoContract] public class DogBarkedEvent : IEvent { [ProtoMember(0)] public ssortingng NameOfDog { get; set; } [ProtoMember(1)] public int Times { get; set; } } //Somewhere in a class far, far away public byte[] Serialize(IMessage message) { using(var stream = new MemoryStream()) { ProtoBuf.Serializer.Serialize(stream, message); return stream.ToArray(); } } public IMessage Deserialize(byte[] data) { using(var stream = new MemoryStream(data)) { return ProtoBuf.Serializer.Deserialize(stream); } } 

Pour vous éclairer un peu: Les événements sérialisés sont écrits dans la persistance. Lors de leur lecture, l’utilisation d’une méthode de désérialisation avec un argument générique n’est pas une option viable (la meilleure solution consiste à spécifier les informations de type en tant que paramètre standard ou à l’aide du contrat commun, IMessage dans ce cas).

Il y a deux manières d’aborder cela. Mon option la moins préférée consiste à utiliser DynamicType=true . Cette option est plus coûteuse et limite la portabilité / la gestion des versions, mais n’exige aucune connaissance préalable des données. Mon option préférée est de déclarer un identifiant fixe par interface, ce qui lui permet de reconnaître les données elles-mêmes. Ceci est montré ci-dessous.

Pour info, DontAskWrapper est parce que Serialize() utilise GetType() ; ce qui signifie qu’il ne détectera pas la base de l’interface. Je suppose que je peux améliorer cela, mais cela fonctionne pour aujourd’hui sur v2:

 using System.Diagnostics; using System.IO; using NUnit.Framework; using ProtoBuf; using ProtoBuf.Meta; namespace Examples.Issues { [TestFixture] public class SO7078615 { [ProtoContract] // treat the interface as a contract // since protobuf-net *by default* doesn't know about type metadata, need to use some clue [ProtoInclude(1, typeof(DogBarkedEvent))] // other concrete messages here; note these can also be defined at runtime - nothing *needs* // to use atsortingbutes public interface IMessage { } public interface IEvent : IMessage { } [ProtoContract] // removed (InferTagFromName = true) - since you are already qualifying your tags public class DogBarkedEvent : IEvent { [ProtoMember(1)] // .proto tags are 1-based; blame google ;p public ssortingng NameOfDog { get; set; } [ProtoMember(2)] public int Times { get; set; } } [ProtoContract] class DontAskWrapper { [ProtoMember(1)] public IMessage Message { get; set; } } [Test] public void RoundTripAnUnknownMessage() { IMessage msg = new DogBarkedEvent { NameOfDog = "Woofy", Times = 5 }, copy; var model = TypeModel.Create(); // could also use the default model, but using(var ms = new MemoryStream()) // separation makes life easy for my tests { var tmp = new DontAskWrapper {Message = msg}; model.Serialize(ms, tmp); ms.Position = 0; ssortingng hex = Program.GetByteSsortingng(ms.ToArray()); Debug.WriteLine(hex); var wrapper = (DontAskWrapper)model.Deserialize(ms, null, typeof(DontAskWrapper)); copy = wrapper.Message; } // check the data is all there Assert.IsInstanceOfType(typeof(DogBarkedEvent), copy); var typed = (DogBarkedEvent)copy; var orig = (DogBarkedEvent)msg; Assert.AreEqual(orig.Times, typed.Times); Assert.AreEqual(orig.NameOfDog, typed.NameOfDog); } } } 

Et voici la même chose sans atsortingbuts:

 public interface IMessage { } public interface IEvent : IMessage { } public class DogBarkedEvent : IEvent { public ssortingng NameOfDog { get; set; } public int Times { get; set; } } class DontAskWrapper { public IMessage Message { get; set; } } [Test] public void RoundTripAnUnknownMessage() { IMessage msg = new DogBarkedEvent { NameOfDog = "Woofy", Times = 5 }, copy; var model = TypeModel.Create(); model.Add(typeof (DogBarkedEvent), false).Add("NameOfDog", "Times"); model.Add(typeof (IMessage), false).AddSubType(1, typeof (DogBarkedEvent)); model.Add(typeof (DontAskWrapper), false).Add("Message"); using (var ms = new MemoryStream()) { var tmp = new DontAskWrapper { Message = msg }; model.Serialize(ms, tmp); ms.Position = 0; ssortingng hex = Program.GetByteSsortingng(ms.ToArray()); Debug.WriteLine(hex); var wrapper = (DontAskWrapper)model.Deserialize(ms, null, typeof(DontAskWrapper)); copy = wrapper.Message; } // check the data is all there Assert.IsInstanceOfType(typeof(DogBarkedEvent), copy); var typed = (DogBarkedEvent)copy; var orig = (DogBarkedEvent)msg; Assert.AreEqual(orig.Times, typed.Times); Assert.AreEqual(orig.NameOfDog, typed.NameOfDog); } 

Notez que dans les deux cas, TypeModel doit être mis en cache et réutilisé. il est thread-safe, donc peut être utilisé de manière agressive en parallèle par différents threads, etc.