您可以在序列化时为一组特定类型执行此操作,方法是构造一个XmlAttributeOverrides,为应用了DefaultValueAttribute 的每个字段或属性指定new XmlAttributes() { XmlDefaultValue = null },然后将其传递给XmlSerializer(Type, XmlAttributeOverrides) 构造函数:
var overrides = new XmlAttributeOverrides();
var attrs = new XmlAttributes() { XmlDefaultValue = null };
overrides.Add(typeToSerialize, propertyNameWithDefaultToIgnore, attrs);
var serializer = new XmlSerializer(typeToSerialize, overrides);
但是请注意,这个important warning from the documentation:
动态生成的程序集
为了提高性能,XML 序列化基础结构动态生成程序集以序列化和反序列化指定类型。基础结构查找并重用这些程序集。此行为仅在使用以下构造函数时发生:
XmlSerializer.XmlSerializer(类型)
XmlSerializer.XmlSerializer(类型,字符串)
如果您使用任何其他构造函数,则会生成同一程序集的多个版本并且永远不会卸载,这会导致内存泄漏和性能下降。最简单的解决方案是使用前面提到的两个构造函数之一。否则,您必须将程序集缓存在 Hashtable 中,如下例所示。
但是,代码中给出的示例并没有给出如何键入哈希表的任何建议。它也不是线程安全的。 (也许它可以追溯到 .Net 1.0?)
以下代码为具有覆盖的 xml 序列化程序创建一个密钥方案,并制造(通过反射)序列化程序,所有属性和字段的 [DefaultValue] 值(如果有)都被覆盖为空,有效地取消了默认值.请注意,在创建空白 XmlAttributes() 对象时,所有属性都设置为 null。使用此 XmlAttributes 对象覆盖时,任何希望保留的属性都需要转移到此新对象中:
public abstract class XmlSerializerKey
{
static class XmlSerializerHashTable
{
static Dictionary<object, XmlSerializer> dict;
static XmlSerializerHashTable()
{
dict = new Dictionary<object, XmlSerializer>();
}
public static XmlSerializer GetSerializer(XmlSerializerKey key)
{
lock (dict)
{
XmlSerializer value;
if (!dict.TryGetValue(key, out value))
dict[key] = value = key.CreateSerializer();
return value;
}
}
}
readonly Type serializedType;
protected XmlSerializerKey(Type serializedType)
{
this.serializedType = serializedType;
}
public Type SerializedType { get { return serializedType; } }
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
return true;
else if (ReferenceEquals(null, obj))
return false;
if (GetType() != obj.GetType())
return false;
XmlSerializerKey other = (XmlSerializerKey)obj;
if (other.serializedType != serializedType)
return false;
return true;
}
public override int GetHashCode()
{
int code = 0;
if (serializedType != null)
code ^= serializedType.GetHashCode();
return code;
}
public override string ToString()
{
return string.Format(base.ToString() + ": for type: " + serializedType.ToString());
}
public XmlSerializer GetSerializer()
{
return XmlSerializerHashTable.GetSerializer(this);
}
protected abstract XmlSerializer CreateSerializer();
}
public abstract class XmlserializerWithExtraTypesKey : XmlSerializerKey
{
static IEqualityComparer<HashSet<Type>> comparer;
readonly HashSet<Type> extraTypes = new HashSet<Type>();
static XmlserializerWithExtraTypesKey()
{
comparer = HashSet<Type>.CreateSetComparer();
}
protected XmlserializerWithExtraTypesKey(Type serializedType, IEnumerable<Type> extraTypes)
: base(serializedType)
{
if (extraTypes != null)
foreach (var type in extraTypes)
this.extraTypes.Add(type);
}
public Type[] ExtraTypes { get { return extraTypes.ToArray(); } }
public override bool Equals(object obj)
{
if (!base.Equals(obj))
return false;
XmlserializerWithExtraTypesKey other = (XmlserializerWithExtraTypesKey)obj;
return comparer.Equals(this.extraTypes, other.extraTypes);
}
public override int GetHashCode()
{
int code = base.GetHashCode();
if (extraTypes != null)
code ^= comparer.GetHashCode(extraTypes);
return code;
}
}
public sealed class XmlSerializerIgnoringDefaultValuesKey : XmlserializerWithExtraTypesKey
{
readonly XmlAttributeOverrides overrides;
private XmlSerializerIgnoringDefaultValuesKey(Type serializerType, IEnumerable<Type> ignoreDefaultTypes, XmlAttributeOverrides overrides)
: base(serializerType, ignoreDefaultTypes)
{
this.overrides = overrides;
}
public static XmlSerializerIgnoringDefaultValuesKey Create(Type serializerType, IEnumerable<Type> ignoreDefaultTypes, bool recurse)
{
XmlAttributeOverrides overrides;
Type [] typesWithOverrides;
CreateOverrideAttributes(ignoreDefaultTypes, recurse, out overrides, out typesWithOverrides);
return new XmlSerializerIgnoringDefaultValuesKey(serializerType, typesWithOverrides, overrides);
}
protected override XmlSerializer CreateSerializer()
{
var types = ExtraTypes;
if (types == null || types.Length < 1)
return new XmlSerializer(SerializedType);
return new XmlSerializer(SerializedType, overrides);
}
static void CreateOverrideAttributes(IEnumerable<Type> types, bool recurse, out XmlAttributeOverrides overrides, out Type[] typesWithOverrides)
{
HashSet<Type> visited = new HashSet<Type>();
HashSet<Type> withOverrides = new HashSet<Type>();
overrides = new XmlAttributeOverrides();
foreach (var type in types)
{
CreateOverrideAttributes(type, recurse, overrides, visited, withOverrides);
}
typesWithOverrides = withOverrides.ToArray();
}
static void CreateOverrideAttributes(Type type, bool recurse, XmlAttributeOverrides overrides, HashSet<Type> visited, HashSet<Type> withOverrides)
{
if (type == null || type == typeof(object) || type.IsPrimitive || type == typeof(string) || visited.Contains(type))
return;
var attrs = new XmlAttributes() { XmlDefaultValue = null };
foreach (var property in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
if (overrides[type, property.Name] == null) // Check to see if overrides for this base type were already set.
if (property.GetCustomAttributes<DefaultValueAttribute>(true).Any())
{
withOverrides.Add(type);
overrides.Add(type, property.Name, attrs);
}
foreach (var field in type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
if (overrides[type, field.Name] == null) // Check to see if overrides for this base type were already set.
if (field.GetCustomAttributes<DefaultValueAttribute>(true).Any())
{
withOverrides.Add(type);
overrides.Add(type, field.Name, attrs);
}
visited.Add(type);
if (recurse)
{
var baseType = type.BaseType;
if (baseType != type)
CreateOverrideAttributes(baseType, recurse, overrides, visited, withOverrides);
}
}
}
然后你会这样称呼它:
var serializer = XmlSerializerIgnoringDefaultValuesKey.Create(typeof(ClassToSerialize), new[] { typeof(ClassToSerialize), typeof(AdditionalClass1), typeof(AdditionalClass2), ... }, true).GetSerializer();
例如,在以下类层次结构中:
public class BaseClass
{
public BaseClass() { Index = 1; }
[DefaultValue(1)]
public int Index { get; set; }
}
public class MidClass : BaseClass
{
public MidClass() : base() { MidDouble = 1.0; }
[DefaultValue(1.0)]
public double MidDouble { get; set; }
}
public class DerivedClass : MidClass
{
public DerivedClass() : base() { DerivedString = string.Empty; }
[DefaultValue("")]
public string DerivedString { get; set; }
}
public class VeryDerivedClass : DerivedClass
{
public VeryDerivedClass() : base() { this.VeryDerivedIndex = -1; }
[DefaultValue(-1)]
public int VeryDerivedIndex { get; set; }
}
默认的XmlSerializer 产生:
<VeryDerivedClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
但自定义序列化程序会产生
<?xml version="1.0" encoding="utf-16"?>
<VeryDerivedClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Index>1</Index>
<MidDouble>1</MidDouble>
<DerivedString />
<VeryDerivedIndex>-1</VeryDerivedIndex>
</VeryDerivedClass>
最后,请注意空值的写入由[XmlElement( IsNullable = true )] 控制,因此空值的写入不受此序列化程序的影响。