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

本文关键字:实现 抽象类 反序列化 二进制 | 更新日期: 2023-09-27 18:00:25

我有一个抽象类,我正在尝试对其具体实现进行序列化和反序列化。在我的抽象基类中,我有这样的:

[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);
}

我认为通过将"已知类型"添加到我的DataContractSerializer中,它将能够找出如何反序列化派生类,但似乎没有。它抱怨错误:

命名空间"中应包含元素"MyAbstractBase"http://schemas.datacontract.org/2004/07/MyApp.Foo'。。遇到名为"SomeOtherClass.MyDerivedClass"、命名空间为"的"Element"http://schemas.datacontract.org/2004/07/MyApp.Foo.Bar"。

你知道我在这里错过了什么吗?

我在一个点网小提琴上做了一个简单的演示:

http://dotnetfiddle.net/W7GCOw

不幸的是,它不会直接在那里运行,因为它不包括System.Runtime.Serialization程序集。但是,如果您将它放入VisualStudio项目中,它会很好地序列化,但会阻碍反序列化。

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

序列化数据时,使用与反序列化相同的重载序列化方法:

DataContractSerializer serializer = new DataContractSerializer(typeof(MyAbstractBase ), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });

此外,在基类周围声明一个KnowType属性,这样它就知道可以反序列化哪些可能的派生类:

[DataContract]
[KnownType(typeof(SomeOtherclass.MyDerivedClass))]
public class MyAbstractBase
{
    [DataMember]
    public string Foo { get; set; }
    // some other abstract methods that derived classes have to implement
}

所以我发现了几个问题。首先,CreateBinaryWriter似乎根本不起作用。所以我放弃了它,直接用serializer.WriteObject(ws,this); 序列化

第二个问题是关于序列化,我这样做了:

DataContractSerializer serializer = new DataContractSerializer(this.GetType(), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });

问题是,我在那里传递的类型不是基类型,而是我调用该函数的任何类型。但在反序列化中,我有这样的:

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或任何东西。

EDIT:我已经解决了data at the root level is invalid问题。用显式的NameNamespace属性装饰我的[DataContract]属性似乎解决了这个问题,而且,作为额外的奖励,由于我大大缩短了名称空间,所以减少了数据的大小。我不知道为什么序列化程序无法处理真正的命名空间。

另一次编辑:最后一道皱纹。我认为名称空间的问题是转移注意力,我只是巧合地让它工作。这里解释了问题的根源(以及解决方案):

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

MemoryStream上执行GetBuffer()时,由于底层数组根据需要调整自身大小的方式,可能会获得多余的null字符。这在序列化的末尾放置了一堆null(可以在数组的基数64后发现为一堆A),这些都是导致反序列化的原因,因为非常令人困惑的The data at the root level is invalid. Line 1, position 1.。这很令人困惑,因为问题一开始根本不是,而是在结束!!!

如果有人感兴趣,通过序列化现在看起来是这样的:

    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;
    }