2013-10-18 64 views
2

我正在爲我正在使用的系統集合編寫一個不可知的查看器。這個查看器將顯示我的數據的通用結構,而不需要知道特定系統的上下文。在反序列化時丟失引用時不會引發異常

我試圖反序列化僅包含一個類型的內存流Foo<T>其中Foo<T>繼承自Foo。從不可知論的觀點來看,我需要的所有數據都在Foo中。 <T>部分是無關緊要的。

類型T在另一個程序集中定義。在正常操作下,系統顯然已加載所有適當的上下文組件。問題是,在運行查看器時,沒有任何上下文組件被加載。當我嘗試反序列化Foo的實例時,我明顯得到一個異常,因爲引用的程序集未加載。

我試圖檢測我是否加載了所有需要的引用程序集,從而知道是否嘗試對數據進行反序列化,或者重新構建了我需要從該類的其他方面獲得的數據。

我知道我可以使用一個非常簡單的異常try/catch塊來做到這一點,但是,這不是一個例外情況。 I 知道當我加載數據時,這將會發生數百次(如果不是數千次),這可能會導致我變成一場噩夢,因爲我喜歡打破異常。我還訂閱了「異常 - 提示在名稱中」的思想流派,因此異常不應構成您的主要案例代碼的一部分。

--------編輯21/10/2013 ------------

看到here一個完整的說明性的例子,但這裏有重要的位:

Foo類,在共同定義:

[Serializable] 
public class Foo 
{ 
    public string Agnostic { get; set; } 
} 

[Serializable] 
public class Foo<T> : Foo 
{ 
    public string Contextual { get; set; } 
} 

上下文保存:

BinaryFormatter bf = new BinaryFormatter(); 
FileInfo tempFile = TempFileGetter.GetTempFile(); 


Foo<Bar> fooBar = new Foo<Bar>(); 
fooBar.Agnostic = "Agnostic"; 
fooBar.Contextual = "Contextual"; 


using (var fs = tempFile.OpenWrite()) 
{ 
    bf.Serialize(fs, fooBar); 
    fs.Flush(); 
} 

不可知論裝載:

BinaryFormatter bf = new BinaryFormatter(); 
FileInfo tempFile = TempFileGetter.GetTempFile(); 

using (var fs = tempFile.OpenRead()) 
{ 
    Foo foo = (Foo)bf.Deserialize(fs); 
    Controls.DataContext = foo; 
} 

我的意思是,沒有什麼火箭科學在這段代碼,而且,如果「不可知」瀏覽器加載上下文觀衆作爲參考,那麼它加載罰款,但是,我並不想這樣做,因爲我們不會總是加載上下文庫。

+0

你能舉一個你的反序列化代碼的例子嗎?你能改變序列化代碼嗎? –

+0

添加代碼來說明問題(完整的可下載版本) –

回答

0

我所做的就是創建一個序列容器,分析它的內容,看看需要哪些引用和單獨序列:

[serializable] 
public class Container 
{ 
    private IEnumerable<object> data; 
    public Container(IEnumerable data); 

    public string[] GetFullyQualifiedReferences();   
} 

所以,你把你喜歡的任何數據在這裏,你會得到一個列表完全合格的程序集名稱,然後單獨存儲。

假設Wibble是序列化的Foo

public class Wibble : ISerializable 
{   

    public string Agnostic { get { return agnostic; } } 

    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     //construct a container based on my data. 
     Container container = new Container(new object[]{myFoo}); 
     info.AddValue("RequiredReferences",container.GetFullyQualifiedReferences()); 

     byte[] containerData = Serialize(container); 
     info.AddValue("TypeUnsafeData",containerData); 

     //store some safe typed versions of the context data, if necessary. 
     info.AddValue("SafeValues", GetSafeValues()) 
    } 

    public Wibble(SerializationInfo info, StreamingContext context) 
    { 
     var requiredAssemblies = 
      (string[])info.GetValue("RequiredReferences",typeof(string[])); 

     if(AreAssembliesLoaded(requiredAssemblies))) 
     { 
      //deserialise the container as normal 
     } 
     else 
     { 
      //instead, load the "safe" data that we previously stored. 
     } 

    } 

} 

我知道這不會使給出的插圖非常有意義的課,但插圖是我的執行問題的一個稍微不完美抽象,但解決方案(我知道它適用於我的實現!)

您可以進入下一步,並在容器中放置一個容器,列出所有容器的特定所需組件,但在此插圖中並不是必需的。

------更新-------

沒有與因爲它代表這個實現一個小問題,那就是如果你更新上下文組件的版本號,解串器不會找到舊的組件。所以,你要麼需要:

提供某種機制,以允許deseralize代碼,看是否能找到組件的現代版本,然後對其進行查詢,

---或---

更新完全限定名稱的等同性,使其對版本控制不敏感。

-1

的BinaryFormatter的包括這些信息,嘗試到一個MemoryStream讀取序列數據,並將其轉換爲一個字符串

// There's probably a better way to do this: 
new String(memoryStream.GetBuffer().Select<byte, char>(b => (char)b).ToArray()); 

對於Assembly.Name.MyObject<int>你最終的東西,看起來像一個痛苦的解析,但它應該做,能夠:

[ÿÿÿÿBAssembly.Name,版本= 1.0.0.0,文化=中性公鑰= nullgMyObject`1 [System.Int32,mscorlib程序,版本= 4.0.0.0,文化=中立,公鑰= b77a5c561934e089]] n1n2str]

或者,如果您有序列化的控制,請首先使用文件中需要的信息序列化對象(例如,只是T的類型和組合),然後是來自BinaryFormatter的數據。 (如果你願意,我可以擴展這個)。

要查看是否加載了任何對象,可以使用Type.GetType("Assembly.Name.Space.ClassName"),因爲如果找不到類型,它將返回null,但也可以使用其他方法列出問題"how to check if namespace, class, or method exists",以代替它。

+0

我不確定我是否適合作爲解決方案,它看起來很像打破序列化引擎的封裝......如果二進制序列化程序使用更改的格式,或者如果我改變了我的序列化引擎,事情就會變得非常激烈,爲什麼... –

+0

如果你改變你的序列化引擎,事情將會隨着任何預先存在的已保存數據而發生變化...但是你是對的,這確實打破了封裝。因此,在調用內置引擎執行大量工作之前,編寫自己的序列化引擎,以便寫入一些元數據(類/程序集名稱)。反序列化可以讀取元數據,檢查該類是否存在,然後將其傳遞給內置引擎(如果適用)。如果您認爲您可能會更改該引擎,那麼您的自定義類可能會包含一個標誌,以說明您正在使用哪個引擎,以便將來可以讀取您的數據。 –

+0

哦,這就是你所做的一切,只是用類結構而不是文件中的數據。 –

相關問題