使用 Protobuf-net 序列化和反序列化实现抽象 类 的对象
Serializing and Deserializing objects that implement abstract classes using Protobuf-net
背景
我正在尝试让下面的 class 序列化和反序列化一个 class,它使用 Protobuf-net 实现了一个抽象 class。但是,反序列化时失败并出现错误:"No parameterless constructor found for AbstractTest".
我做错了什么?
我试过的
我花了很多时间研究答案,但很少有问题涉及我问题的抽象性质。
最小示例
public class TestRunner
{
public void Go()
{
string encoded = Serialize<ConcreteTest>(new ConcreteTest());
object ob = Deserialize(new System.IO.MemoryStream(Encoding.ASCII.GetBytes(encoded)));
}
public static string Serialize<T>(T data)
{
MemoryStream outputStream = new MemoryStream();
Serializer.Serialize<T>(outputStream, data);
outputStream.Position = 0;
StreamReader outputReader = new StreamReader(outputStream);
return outputReader.ReadToEnd();
}
public static object Deserialize(Stream data)
{
AbstractTest test = Serializer.Deserialize<AbstractTest>(data);
//Error from the line above: No parameterless constructor found for AbstractTest
if (test.ID == 3)
{
return Serializer.Deserialize<ConcreteTest>(data);
}
throw new Exception("We don't know how to handle the message type if I got here!");
}
}
[ProtoBuf.ProtoContract]
[ProtoBuf.ProtoInclude(25, typeof(ConcreteTest))]
public abstract class AbstractTest
{
public AbstractTest()
{
}
[ProtoBuf.ProtoMember(1)]
public int ID = 3;
public abstract void Test();
}
[ProtoBuf.ProtoContract]
public class ConcreteTest : AbstractTest
{
[ProtoBuf.ProtoMember(2)]
public int ID2 = 4;
public override void Test()
{
MasterLog.DebugWriteLine("It worked!");
}
}
最初的问题是您通过向后使用 Encoding
损坏了数据。一个 Encoding
将任意文本转换为格式化二进制(意思是:根据 "encoding" 的规则格式化),并将格式化二进制转换回任意文本。它旨在描述如何将文本写入流。您已经反向使用它,意思是:您已经使用它将任意二进制文件 以不同的格式 转换为字符串。您可能实际上还使用了两种不同的编码(StreamReader
几乎肯定不会默认使用 ASCII)。
所以:你不能在这里使用 Encoding
。您可以使用的是base-64;例如:
using (var outputStream = new MemoryStream())
{
Serializer.Serialize<T>(outputStream, data);
return Convert.ToBase64String(outputStream.GetBuffer(),
0, (int)outputStream.Length);
}
您可以使用 Convert.FromBase64String
反转此操作:
using (var ms = new System.IO.MemoryStream(Convert.FromBase64String(encoded)))
{
object ob = Deserialize(ms);
Console.WriteLine(ob.ToString());
}
还有一个问题,你反序列化一个流两次而不倒带,这意味着第二次反序列化工作在零字节上,这意味着它将尝试实例化一个抽象类型;所以你还需要修复 Deserialize
方法:
public static object Deserialize(Stream data)
{
AbstractTest test = Serializer.Deserialize<AbstractTest>(data);
return test;
}
完成后,一切正常。
背景
我正在尝试让下面的 class 序列化和反序列化一个 class,它使用 Protobuf-net 实现了一个抽象 class。但是,反序列化时失败并出现错误:"No parameterless constructor found for AbstractTest".
我做错了什么?
我试过的
我花了很多时间研究答案,但很少有问题涉及我问题的抽象性质。
最小示例
public class TestRunner
{
public void Go()
{
string encoded = Serialize<ConcreteTest>(new ConcreteTest());
object ob = Deserialize(new System.IO.MemoryStream(Encoding.ASCII.GetBytes(encoded)));
}
public static string Serialize<T>(T data)
{
MemoryStream outputStream = new MemoryStream();
Serializer.Serialize<T>(outputStream, data);
outputStream.Position = 0;
StreamReader outputReader = new StreamReader(outputStream);
return outputReader.ReadToEnd();
}
public static object Deserialize(Stream data)
{
AbstractTest test = Serializer.Deserialize<AbstractTest>(data);
//Error from the line above: No parameterless constructor found for AbstractTest
if (test.ID == 3)
{
return Serializer.Deserialize<ConcreteTest>(data);
}
throw new Exception("We don't know how to handle the message type if I got here!");
}
}
[ProtoBuf.ProtoContract]
[ProtoBuf.ProtoInclude(25, typeof(ConcreteTest))]
public abstract class AbstractTest
{
public AbstractTest()
{
}
[ProtoBuf.ProtoMember(1)]
public int ID = 3;
public abstract void Test();
}
[ProtoBuf.ProtoContract]
public class ConcreteTest : AbstractTest
{
[ProtoBuf.ProtoMember(2)]
public int ID2 = 4;
public override void Test()
{
MasterLog.DebugWriteLine("It worked!");
}
}
最初的问题是您通过向后使用 Encoding
损坏了数据。一个 Encoding
将任意文本转换为格式化二进制(意思是:根据 "encoding" 的规则格式化),并将格式化二进制转换回任意文本。它旨在描述如何将文本写入流。您已经反向使用它,意思是:您已经使用它将任意二进制文件 以不同的格式 转换为字符串。您可能实际上还使用了两种不同的编码(StreamReader
几乎肯定不会默认使用 ASCII)。
所以:你不能在这里使用 Encoding
。您可以使用的是base-64;例如:
using (var outputStream = new MemoryStream())
{
Serializer.Serialize<T>(outputStream, data);
return Convert.ToBase64String(outputStream.GetBuffer(),
0, (int)outputStream.Length);
}
您可以使用 Convert.FromBase64String
反转此操作:
using (var ms = new System.IO.MemoryStream(Convert.FromBase64String(encoded)))
{
object ob = Deserialize(ms);
Console.WriteLine(ob.ToString());
}
还有一个问题,你反序列化一个流两次而不倒带,这意味着第二次反序列化工作在零字节上,这意味着它将尝试实例化一个抽象类型;所以你还需要修复 Deserialize
方法:
public static object Deserialize(Stream data)
{
AbstractTest test = Serializer.Deserialize<AbstractTest>(data);
return test;
}
完成后,一切正常。