2013-08-28 23 views
1

Microsoft XmlSerializer中似乎存在一個錯誤/不一致:如果您有一個標記爲System.ComponentModel.DefaultValue屬性的屬性,則該屬性不會被序列化。公平 - 這可以被看作是一種預期的行爲。如何使XmlSerializer.Deserialize正確處理DefaultAttribute?

問題是反序列化時不尊重相同的屬性。下面的代碼說明了這個問題。

問題是我怎麼能繞過這個?我有潛在的數百個業務類,在UI層(視圖)中使用默認值,因此構造函數中的默認值初始化不是一個選項。它必須是通用的。我可以創建一個全新的默認屬性,但它看起來像重複的工作。您是否看到了重寫XmlSerializer行爲的方法,還是應該只使用另一個可以更好地完成這項工作的序列化程序?

的示例代碼:

public class DefaultValueTestClass 
{ 
    [System.ComponentModel.DefaultValue(10000)] 
    public int Foo { get; set; } 
} 

[TestMethod] 
public void SimpleDefaultValueTest() 
{ 
    // Create object and set the property value TO THE DEFAULT 
    var before = new DefaultValueTestClass(); 
    before.Foo = 10000; 
    // Serialize => xml 
    var serializer = new System.Xml.Serialization.XmlSerializer(typeof(DefaultValueTestClass)); 
    string xml; 
    using (var stream = new System.IO.StringWriter()) 
    { 
     serializer.Serialize(stream, before); 
     xml = stream.ToString(); 
    } 

    // Deserialize the same object 
    DefaultValueTestClass after; 
    using (var reader = new System.IO.StringReader(xml)) 
    { 
     after = (DefaultValueTestClass)serializer.Deserialize(reader); 
    } 

    // before.Foo = 10000 
    // after.Foo = 0 
    Assert.AreEqual(before.Foo, after.Foo); 
} 
+0

我想你可以通過反射,在反序列化,使用DefaultValue屬性獲取類和它的超類的所有屬性,爲每個屬性獲取值,將其轉換爲正確的類型,並且仍然通過反射,在反序列化的對象上調用屬性的setter方法,抓取值作爲參數 – jbl

+0

@jbl是的,這是我在UI層做的。但是我不能在XmlSerializer.Deserialize之後執行它,因爲我不知道Foo = 0是因爲它沒有被序列化,還是因爲它被用戶設置爲0.這樣,我需要在反序列化之前插入串行器發生。並不知道如何(除了在構造函數中,因爲大量的類,我不想這樣做)。 – Ope

+0

也許這可以用一些動態代理完成,這些動態代理將實現IXmlSerializable接口來代替你的類。我必須承認它只是隨機的想法,雖然...... – jbl

回答

0

這是你的工作落實缺省值; [DefaultValue]只是說「這是默認的,你不需要擔心這個」 - 它不是適用它。這並不適用於只是XmlSerializer,但對核心System.ComponentModel API到[DefaultValue]屬於(驅動之類的大膽/不大膽PropertyGrid等)

基本上是:

public class DefaultValueTestClass 
{ 
    public DefaultValueTestClass() 
    { 
     Foo = 10000; 
    } 
    [DefaultValue(10000)] 
    public int Foo { get; set; } 
} 

將在工作你期望的方式。如果你想它序列它是否是特定的值,那麼正確的實現方式是:

public class DefaultValueTestClass 
{ 
    public DefaultValueTestClass() 
    { 
     Foo = 10000; 
    } 
    public int Foo { get; set; } 
} 

如果您想保留[DefaultValue],但希望它永遠序列,則:

public class DefaultValueTestClass 
{ 
    [DefaultValue(10000)] 
    public int Foo { get; set; } 

    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] 
    public bool ShouldSerializeFoo() { return true; } 
} 

其中ShouldSerialize*是來自System.ComponentModel的另一種模式,它被幾個序列化器識別。


而且這裏的一些UI代碼表明XmlSerializer其實就是做完全一樣的東西,UI代碼(建於System.ComponentModel)一直做:

using System.ComponentModel; 
using System.Windows.Forms; 
static class Program 
{ 
    [System.STAThread] 
    static void Main() 
    { 
     Application.EnableVisualStyles(); 
     using (var form = new Form()) 
     using (var grid = new PropertyGrid()) 
     { 
      grid.Dock = DockStyle.Fill; 
      var obj = new DefaultValueTestClass 
      { // TODO - try with other numbers to 
       // see bold/not bold 
       Foo = 10000 
      }; 
      // note in the grid the value is shown not-bold; that is 
      // because System.ComponentModel is saying 
      // "this property doesn't need to be serialized" 
      // - or to show it more explicitly: 
      var prop = TypeDescriptor.GetProperties(obj)["Foo"]; 
      bool shouldSerialize = prop.ShouldSerializeValue(obj); 
      // ^^^ false, because of the DefaultValueAttribute 
      form.Text = shouldSerialize.ToString(); // win title 

      grid.SelectedObject = obj; 
      form.Controls.Add(grid); 
      Application.Run(form);    
     } 
    } 
} 

public class DefaultValueTestClass 
{ 
    [System.ComponentModel.DefaultValue(10000)] 
    public int Foo { get; set; } 
} 
+0

正如我上面所說:「我有潛在的數百個業務類與UI層(視圖)中使用的默認值,所以在構造函數中的默認值初始化不是一個選項。「我在用戶界面中使用的屬性就像屬性網格等在舊的WinForms時代所做的一樣。 – Ope

+0

@Ope是否看到我在底部編輯過的第三個選項,它不使用它?另外:'XmlSerializer'在WinForms中使用和'PropertyGrid'完全相同的規則:所以(我並不是指這種貶義 - 只是清楚地說明當前狀態是什麼)*你實際上*說的是「我已經不正確地使用它的年齡,但現在它*重要*,我一直在錯誤地使用它「 - 這不是真的'XmlSerializer'的錯。希望'ShouldSerialize *'應該足以讓你工作。 –

+0

@Ope我將在UI代碼的插圖中以完全相同的方式進行編輯 –