簡體   English   中英

抽象類的二進制反序列化實現

[英]Binary Deserializing implementations of an abstract class

我有一個抽象類,我正在嘗試序列化和反序列化其具體實現。 在我的抽象基類中,我有以下內容:

[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添加“已知類型”,可以弄清楚如何反序列化派生類,但事實並非如此。 它抱怨錯誤:

從名稱空間' http://schemas.datacontract.org/2004/07/MyApp.Foo '期待元素'MyAbstractBase'..遇到名稱為'SomeOtherClass.MyDerivedClass'的'元素',名稱空間為' http://schemas.datacontract。 org / 2004/07 / MyApp.Foo.Bar '。

所以知道我在這里缺少什么嗎?

我在此處的點網小提琴上將問題的簡單演示放在一起:

http://dotnetfiddle.net/W7GCOw

不幸的是,它不會直接在此處運行,因為它不包含System.Runtime.Serialization程序集。 但是,如果將其放到Visual Studio項目中,它將很好地進行序列化,但會反序列化。

序列化數據時,請使用與反序列化相同的重載方法進行序列化:

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
}

因此,我確定了幾個問題。 首先是CreateBinaryWriter似乎根本不起作用。 所以我刪除了它,並直接使用serializer.WriteObject(ws,this);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. The data at the root level is invalid. Line 1, position 1.這很奇怪,因為我已經比較了來自序列化的數據和進入反序列化的數據,它們是絕對相同的。 沒有流氓BOM或其他任何東西。

編輯 :我已經data at the root level is invalid解決了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()時,由於基礎數組根據需要自行調整大小,因此可以獲得多余的空字符。 這會在序列化的末尾放置一堆空值(可以在對數組進行基數64'處理后將其視為一串A ),這些都是The data at the root level is invalid. Line 1, position 1.使用非常混亂The data at the root level is invalid. Line 1, position 1.破壞反序列化的原因The data at the root level is invalid. Line 1, position 1. 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;
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM