2009-10-21 73 views
0

我注意到XmlSerializer更容易添加新成員,刪除現有的成員等序列化類型。哪個序列化程序最容易改變.NET中的序列化類型?

當我用BinaryFormatter做了這件事,並試圖反序列化舊數據時,它拋出了一個異常。

還有什麼其他替代方法可以寬恕選項,即不會拋出異常的只使用默認值,跳過它們等等?

協議緩衝區在這方面是否寬容?

回答

2

你可以從ISerializable繼承你的類並定義一個自定義的GetObjectData。我沒有測試過這個,但是這樣的類可能會從二進制格式反序列化,即使這個類已經發生了變化。

編輯

我只是確認這工作。您可以使用如下例所示的代碼來明確定義對象是如何序列化和反序列化的。然後,由您決定使這些方法與您班級的舊版本一起工作。我通過將一個Cereal實例序列化爲一個二進制文件,然後對該類進行更改並讀取該文件以進行反序列化來測試此操作。

[Serializable] 
private class Cereal : ISerializable 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 

    public Cereal() 
    { 
    } 

    protected Cereal(SerializationInfo info, StreamingContext context) 
    { 
     Id = info.GetInt32 ("Id"); 
     Name = info.GetString ("Name"); 
    } 

    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     info.AddValue ("Id", Id); 
     info.AddValue ("Name", Name); 
    } 
} 
+0

是的,這正是ISerializable存在的原因 - 所以你可以處理以前的格式。 – 2009-10-21 17:47:33

0

我強烈建議您做自己的序列化,以便您有獨立於語言方案的定義明確的文件格式。

4

您提到二進制文件,實際上BinaryFormattervery brittle這裏。問題是BinaryFormatter是基於類型和字段的。相反,你要基於合同串,如XmlSerialzierDataContractSerializer(3.0)等

或者二進制,protobuf-net是谷歌的「協議緩衝區」線格式的C#實現,但隨着.NET重新實現線; (注意:我是作者...)。

它是(像其他人一樣)基於數據合同的,而不是<CustomerName>asdasd</CustomerName>等,它使用數字標籤來標識事物;所以:

[ProtoContract] 
public class Customer { 
    [ProtoMember(1)] 
    public string Name {get;set;} 

    // ... 
} 

當你添加更多成員時,你給他們新的唯一編號;這使它可以在不依賴任何名稱的情況下擴展,加上它非常快; -p與XmlSerializer一樣,它會忽略它不期望的事情(或者它可以存儲它們以安全地往返意外數據),並支持相同的默認東西。你甚至可以使用現有的XML屬性:

[XmlType] 
public class Customer { 
    [XmlElement(Order=1)] 
    public string Name {get;set;} 

    // ... 
} 

我可以談論這個問題了一整天,所以我之前[太晚]最好閉嘴。

+0

謝謝馬克,請隨時多談:)有一件事我想知道的是,如果說你改變了類型並添加了新的獨特數字,你以後可以擺脫舊的數字嗎?就像如果你使用1,2,3,4和現在5來代替2的新成員。你以後可以得到2代表5嗎?我只是認爲這對我的目的可能更清潔? – 2009-10-21 17:50:01

+1

我從這個評論中學到了兩件事。你是英國人,相當自信。 :D – Letterman 2009-10-21 18:03:40

+0

哈哈,你的意思是馬克吧? – 2009-10-21 18:05:04

0

我發現從長遠來看,二進制格式化程序是最耐用的。

它提供了出色的向前兼容性。也就是說,如果將文件升級到新版本,它將不適用於舊版本的解串器。

我通常創建一些我想用於序列化的簡單數據類。當我需要更改類時,我實現了OnDeserialized/OnDeserializing方法。這允許數據升級。

二進制格式化程序不要求你有一個公共setter你的屬性,這對我來說有時是一個大問題。

[Serializable] 
    public class data 
    { 
     private int m_MyInteger; 
     // New field 
     private double m_MyDouble; 

     [OnDeserializing] 
     internal void OnDeserializing(StreamingContext context) 
     { 
     // some good default value 
     m_MyDouble = 5; 
     } 

     public int MyInteger 
     { 
     get{ return m_MyInteger; } 
     set { m_MyInteger = value; } 
     } 
    }