如何使XmlSerializer.Deserialize正确处理DefaultAttribute?

时间:2013-08-28 12:36:35

标签: c# serialization xmlserializer

Microsoft XmlSerializer中似乎存在错误/不一致:如果您有一个标有System.ComponentModel.DefaultValue属性的属性,则不会序列化。足够公平 - 这可以被视为预期的行为。

问题是反序列化时不遵守相同的属性。下面的代码说明了这个问题。

问题是我怎么能绕过这个?我有可能有数百个业务类在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);
}

1 个答案:

答案 0 :(得分:0)

你的工作来实现默认值; [DefaultValue]只是说“这是默认设置,您不必担心这个问题” - 它不会应用它。这不仅适用于XmlSerializer,还适用于System.ComponentModel所属的核心[DefaultValue] API(驱动PropertyGrid中的粗体/非粗体等)< / p>

基本上:

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中可被多个序列化程序识别的另一种模式。


这里有一些用户界面代码,表明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; }
}