我有一个抽象类,我试图序列化和反序列化的具体实现。在我的抽象基类中,我有这个:
[DataContract]
public class MyAbstractBase
{
[DataMember]
public string Foo { get; set; }
// some other abstract methods that derived classes have to implement
}
对于那个类,我添加了一个序列化方法:
public string SerializeBase64()
{
// Serialize to a base 64 string
byte[] bytes;
long length = 0;
MemoryStream ws = new MemoryStream();
DataContractSerializer serializer = new DataContractSerializer(this.GetType());
XmlDictionaryWriter binaryDictionaryWriter = XmlDictionaryWriter.CreateBinaryWriter(ws);
serializer.WriteObject(binaryDictionaryWriter, this);
binaryDictionaryWriter.Flush();
length = ws.Length;
bytes = ws.GetBuffer();
string encodedData = bytes.Length + ":" + Convert.ToBase64String(bytes, 0, bytes.Length, Base64FormattingOptions.None);
return encodedData;
}
这似乎工作正常,因为它会产生"某些东西"并没有真正抛出任何错误。
当然,问题在于反序列化。我补充说:
public static MyAbstractBase DeserializeBase64(string s)
{
int p = s.IndexOf(':');
int length = Convert.ToInt32(s.Substring(0, p));
// Extract data from the base 64 string!
byte[] memorydata = Convert.FromBase64String(s.Substring(p + 1));
MemoryStream rs = new MemoryStream(memorydata, 0, length);
DataContractSerializer serializer = new DataContractSerializer(typeof(MyAbstractBase ), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });
XmlDictionaryReader binaryDictionaryReader = XmlDictionaryReader.CreateBinaryReader(rs, XmlDictionaryReaderQuotas.Max);
return (MyAbstractBase)serializer.ReadObject(binaryDictionaryReader);
}
我想通过添加&#34;已知类型&#34;到我的DataContractSerializer
,它将能够弄清楚如何反序列化派生类,但似乎它没有。它抱怨错误:
期待元素&#39; MyAbstractBase&#39;来自命名空间&#39; http://schemas.datacontract.org/2004/07/MyApp.Foo&#39; ..遇到&#39;元素&#39;名称&#39; SomeOtherClass.MyDerivedClass&#39;,名称空间&#39; http://schemas.datacontract.org/2004/07/MyApp.Foo.Bar&#39;。
所以我知道我在这里缺少什么?
我在这里用点网小提琴简单地演示了这个问题:
http://dotnetfiddle.net/W7GCOw
不幸的是,它不会直接在那里运行,因为它不包含System.Runtime.Serialization
程序集。但是,如果将它放入Visual Studio项目中,它将序列化很好,但在反序列化时会很糟糕。
答案 0 :(得分:0)
序列化数据时,请使用与反序列化相同的重载方法进行序列化:
DataContractSerializer serializer = new DataContractSerializer(typeof(MyAbstractBase ), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });
此外,声明基类周围的KnownType属性,以便它知道它可能反序列化的可能派生类:
[DataContract]
[KnownType(typeof(SomeOtherclass.MyDerivedClass))]
public class MyAbstractBase
{
[DataMember]
public string Foo { get; set; }
// some other abstract methods that derived classes have to implement
}
答案 1 :(得分:0)
所以我发现了一些问题。首先,CreateBinaryWriter
似乎根本不起作用。所以我放弃了它,直接用serializer.WriteObject(ws,this);
第二个问题是关于序列化我做了这个:
DataContractSerializer serializer = new DataContractSerializer(this.GetType(), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });
问题在于,通过那里的I型不是基本类型,它是我调用此函数的任何类型。但在反序列化中我有这个:
DataContractSerializer serializer = new DataContractSerializer(typeof(MyAbstractBase ), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });
这不是同一个序列化程序。类型不同。所以将两者都更改为typeof(MyAbstractBase)
修复了我的简单示例中的问题。
当然,在我的真实项目中,我仍然会因为使用The data at the root level is invalid. Line 1, position 1.
进行反序列化而抱怨错误,这很奇怪,因为我比较了序列化和数据的数据。反序列化,它们完全相同。没有流氓BOM或任何东西。
编辑:我已解决了data at the root level is invalid
问题。似乎用明确的[DataContract]
和Name
属性来装饰我的Namespace
属性解决了这个问题,并且作为一个额外的好处,减少了我的数据的大小,因为我大大缩短了名称空间。串行器无法应对真正命名空间的原因我还不知道。
另一个编辑:最后一个皱纹。我认为命名空间的东西是一个红色的鲱鱼,我只是纯粹的巧合才使它 。问题的根源(以及解决方案)在这里解释:
当您在GetBuffer()
上执行MemoryStream
时,由于底层数组根据需要调整自身大小的方式,因此可以获得多余的空字符。这会在序列化结束时放置一堆空值(在基数为64的数组之后可以发现它是一堆A
)这些正在搞乱与非常令人困惑The data at the root level is invalid. Line 1, position 1.
。令人困惑,因为问题根本不是,它位于 END !!!
如果有人感兴趣,通过序列化现在看起来像这样:
public string SerializeBase64()
{
// Serialize to a base 64 string
byte[] bytes;
long length = 0;
using (MemoryStream ws = new MemoryStream())
{
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(ws);
DataContractSerializer serializer = new DataContractSerializer(typeof(MyAbstractBase ), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });
serializer.WriteObject(writer, this);
writer.Flush();
length = ws.Length;
// Note: https://msmvps.com/blogs/peterritchie/archive/2009/04/29/datacontractserializer-readobject-is-easily-confused.aspx
// We need to trim nulls from the buffer produced by the serializer because it'll barf on them when it tries to deserialize.
bytes = new byte[ws.Length];
Array.Copy(ws.GetBuffer(), bytes, bytes.Length);
}
string encodedData = bytes.Length + ":" + Convert.ToBase64String(bytes, 0, bytes.Length, Base64FormattingOptions.None);
return encodedData;
}