二进制反序列化抽象类的实现

时间:2014-04-25 20:48:17

标签: c# serialization

我有一个抽象类,我试图序列化和反序列化的具体实现。在我的抽象基类中,我有这个:

[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项目中,它将序列化很好,但在反序列化时会很糟糕。

2 个答案:

答案 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属性解决了这个问题,并且作为一个额外的好处,减少了我的数据的大小,因为我大大缩短了名称空间。串行器无法应对真正命名空间的原因我还不知道。

另一个编辑:最后一个皱纹。我认为命名空间的东西是一个红色的鲱鱼,我只是纯粹的巧合才使它 。问题的根源(以及解决方案)在这里解释:

https://msmvps.com/blogs/peterritchie/archive/2009/04/29/datacontractserializer-readobject-is-easily-confused.aspx

当您在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;
    }