c#-如何使用protobuf-net嵌入类型信息以进行反序列化?
作者:互联网
我希望能够以保留/嵌入类型信息的方式序列化IMessage的具体实例(类似于Json.NET中的可用信息),以便在反序列化时可以使用类型信息来实现这些信息具体实例.我很清楚下面的反序列化方法不起作用.任何有关如何更改它们以使它们起作用的指导将不胜感激.
public interface IMessage {}
public interface IEvent : IMessage {}
[ProtoContract]
public class DogBarkedEvent : IEvent {
[ProtoMember(0)]
public string 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<IMessage>(stream, message);
return stream.ToArray();
}
}
public IMessage Deserialize(byte[] data) {
using(var stream = new MemoryStream(data)) {
return ProtoBuf.Serializer.Deserialize<IMessage>(stream);
}
}
阐明一下:序列化的事件被写入持久性.读取它们时,使用带有通用参数的反序列化方法不是一个可行的选择(最好的做法是将类型信息指定为常规参数或使用公共协定(在这种情况下为IMessage)).
解决方法:
有两种解决方法:我最不推荐的选择是使用DynamicType = true-这更昂贵,并且限制了可移植性/版本化,但是对预先了解数据没有任何要求.我的首选方法是为每个接口声明一个固定的标识符,使其能够识别数据本身.如下所示.
对于信息,DontAskWrapper是因为Serialize()使用GetType();.这意味着它不会发现接口库.我怀疑我可以改善这一点,但这在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 attributes
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 string 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;
string hex = Program.GetByteString(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);
}
}
}
这是没有属性的同一件事:
public interface IMessage { }
public interface IEvent : IMessage { }
public class DogBarkedEvent : IEvent
{
public string 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;
string hex = Program.GetByteString(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);
}
请注意,在两种情况下,都应将TypeModel缓存并重新使用.它是线程安全的,因此可以由不同的线程等积极地并行使用.
标签:serialization,protobuf-net,c,net 来源: https://codeday.me/bug/20191208/2088519.html