如何告诉XmlSerializer始终使用序列化属性?

我正在使用DefaultValue属性来获取正确的PropertyGrid行为(它以粗体显示与默认值不同的值)。 现在,如果我想使用XmlSerializer序列化显示的对象,则xml-file中将没有具有默认值的属性的条目。

告诉XmlSerializer序列化这些仍然是最简单的方法是什么?

我需要它来支持“版本”,所以当我稍后在代码中更改默认值时 – serialized属性获取它已序列化的值,而不是“最新”的值。 我可以考虑以下事项:

  • 覆盖PropertyGrid行为(使用自定义属性,因此它将被XmlSerializer忽略);
  • 做一些自定义的xml-serialization,忽略DefaultValue ;
  • 在将对象传递给XmlSeriazer之前对其执行某些操作,以便它不再包含DefaultValue

但是我有可能错过了一些秘密财产,它可以做到这一点而没有太大的痛苦= D.

这是我想要的一个例子:

  private bool _allowNegative = false; ///  /// Get or set if negative results are allowed ///  [Category(CategoryAnalyse)] [Admin] [TypeConverter(typeof(ConverterBoolOnOff))] //[DefaultValue(false)] *1 public bool AllowNegative { get { return _allowNegative; } set { _allowNegative = value; ConfigBase.OnConfigChanged(); } } //public void ResetAllowNegative() { _allowNegative = false; } *2 //public bool ShouldSerializeAllowNegative() { return _allowNegative; } *3 //public bool ShouldSerializeAllowNegative() { return true; } *4 

如果我取消注释(* 1),那么我在PropertyGrid有所需的效果 – 具有默认值的属性以普通文本显示,否则文本为粗体。 但是, XmlSerializer 不会将具有默认值的属性放入xml文件中,这是不好的 (我正在尝试修复它)。

如果我取消注释(* 2)和(* 3),那么它与取消注释(* 1)完全相同。

如果我取消注释(* 2)和(* 4),那么XmlSerializer将始终将属性放入xml文件中,但这是因为它们不再具有默认值而且PropertyGrid以粗体文本显示所有值。

只要您不需要Xml中的属性,如果您使用DataContractSerializer,您将获得所需的行为。

 [DataContract] public class Test { [DataMember] [DefaultValue(false)] public bool AllowNegative { get; set; } } void Main() { var sb2 = new StringBuilder(); var dcs = new DataContractSerializer(typeof(Test)); using(var writer = XmlWriter.Create(sb2)) { dcs.WriteObject(writer, new Test()); } Console.WriteLine(sb2.ToString()); } 

产生(减去命名空间等)

  false  

您可以使用两个属性:

 // For property grid only: [Category(CategoryAnalyse)] [TypeConverter(typeof(ConverterBoolOnOff))] [DefaultValue(false)] [XmlIgnore] public bool AllowNegative { get { return _allowNegative; } set { _allowNegative = value; ConfigBase.OnConfigChanged(); } } // For serialization: [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] [TypeConverter(typeof(ConverterBoolOnOff))] [XmlElement("AllowNegative")] public bool AllowNegative_XML { get { return _allowNegative; } set { _allowNegative = value; ConfigBase.OnConfigChanged(); } } 

我相信你要找的是ShouldSerialize()Reset() 。 使用它们可以将您的类扩展一些(每个属性有两个函数),但是,它可以实现您正在寻找的特定内容。

这是一个简单的例子:

 // your property variable private const String MyPropertyDefault = "MyValue"; private String _MyProperty = MyPropertyDefault; // your property // [DefaultValueAttribute("MyValue")] - cannot use DefaultValue AND ShouldSerialize()/Reset() public String MyProperty { get { return _MyProperty; } set { _MyProperty = value; } } // IMPORTANT! // notice that the function name is "ShouldSerialize..." followed // by the exact (!) same name as your property public Boolean ShouldSerializeMyProperty() { // here you would normally do your own comparison and return true/false // based on whether the property should be serialized, however, // in your case, you want to always return true when serializing! // IMPORTANT CONDITIONAL STATEMENT! if (!DesignMode) return true; // always return true outside of design mode (is used for serializing only) else return _MyProperty != MyPropertyDefault; // during design mode, we actually compare against the default value } public void ResetMyProperty() { _MyProperty = MyPropertyDefault; } 

请注意,因为您希望保持PropertyGridfunction,所以在ShouldSerialize()函数时,您必须知道是否正在序列化。 我建议你实现某种在序列化时设置的控制标志,因此总是return true


请注意, 不能DefaultValue属性与ShouldSerialize()Reset()函数结合使用( 您只能使用或者 )。


编辑:添加ShouldSerialize()函数的说明。

因为当前无法序列化默认值并让PropertyGrid知道属性具有其默认值,所以必须实现一个条件来检查您是否处于设计模式。

假设您的类派生自ComponentControl ,您将拥有一个DesignMode属性,该属性仅由Visual Studio在设计时设置。 条件如下:

 if (!DesignMode) return true; // always return true outside of design mode (is used for serializing only) else return _MyProperty != MyPropertyDefault; // during design mode, we actually compare against the default value 

编辑2:我们不是在谈论Visual Studio的设计模式。

考虑到上面的代码,创建另一个名为IsSerializing属性。 调用XmlSerializer.Serialize 之前IsSerializing属性设置为true ,并在之后取消设置。

最后,将if (!DesignMode)条件语句更改为if (IsSerializing)

可以使用XmlAttributeOverrides覆盖XmlSerializer此行为

我从这里借用了这个想法:

 static public XmlAttributeOverrides GetDefaultValuesOverrides(Type type) { XmlAttributeOverrides explicitOverrides = new XmlAttributeOverrides(); PropertyDescriptorCollection c = TypeDescriptor.GetProperties(type); foreach (PropertyDescriptor p in c) { AttributeCollection attributes = p.Attributes; DefaultValueAttribute defaultValue = (DefaultValueAttribute)attributes[typeof(DefaultValueAttribute)]; XmlIgnoreAttribute noXML = (XmlIgnoreAttribute)attributes[typeof(XmlIgnoreAttribute)]; XmlAttributeAttribute attribute = (XmlAttributeAttribute)attributes[typeof(XmlAttributeAttribute)]; if ( defaultValue != null && noXML == null ) { XmlAttributeAttribute xmlAttribute = new XmlAttributeAttribute(attribute.AttributeName); XmlAttributes xmlAttributes = new XmlAttributes(); xmlAttributes.XmlAttribute = xmlAttribute; explicitOverrides.Add(userType, attribute.AttributeName, xmlAttributes); } } return explicitOverrides; } 

并使我自己成为一个属性来装饰应该发出默认值的类。 如果你想为所有课程做这件事,我相信你可以适应整个概念。

 Public Class EmitDefaultValuesAttribute Inherits Attribute Private Shared mCache As New Dictionary(Of Assembly, XmlAttributeOverrides) Public Shared Function GetOverrides(assembly As Assembly) As XmlAttributeOverrides If mCache.ContainsKey(assembly) Then Return mCache(assembly) Dim xmlOverrides As New XmlAttributeOverrides For Each t In assembly.GetTypes() If t.GetCustomAttributes(GetType(EmitDefaultValuesAttribute), True).Count > 0 Then AddOverride(t, xmlOverrides) End If Next mCache.Add(assembly, xmlOverrides) Return xmlOverrides End Function Private Shared Sub AddOverride(t As Type, xmlOverrides As XmlAttributeOverrides) For Each prop In t.GetProperties() Dim defaultAttr = prop.GetCustomAttributes(GetType(DefaultValueAttribute), True).FirstOrDefault() Dim xmlAttr As XmlAttributeAttribute = prop.GetCustomAttributes(GetType(XmlAttributeAttribute), True).FirstOrDefault() If defaultAttr IsNot Nothing AndAlso xmlAttr IsNot Nothing Then Dim attrs As New XmlAttributes '= {New XmlAttributeAttribute} attrs.XmlAttribute = xmlAttr ''overide.Add(t, xmlAttr.AttributeName, attrs) xmlOverrides.Add(t, prop.Name, attrs) End If Next End Sub 

因为xsd.exe生成部分类,所以可以在单独的文件中添加此EmitDefaultValuesAttribute

  Public MyClass Public Property SubClass() As MySubClass End Class  Public MySubClass End Class 

用法如下:

 Dim serializer As New XmlSerializer(GetType(MyClass), EmitDefaultValuesAttribute.GetOverrides(GetType(MyClass).Assembly))