首页 > 解决方案 > 在 C# 中删除 XML 序列化的 DefaultValue 属性

问题描述

我正在将一个对象序列化为 XML,该 XML 具有DefaultValue某些属性的属性。在某些情况下,我想在序列化期间禁用所有这些默认值。有没有办法在运行时删除属性?

[Serializable]
[XmlType(TypeName = "MyType")]
public class MyType
{
    public MyType()
    {
        MyValue = false;
    }

    [XmlElement(ElementName = "myValue", Form = XmlSchemaForm.Unqualified)]
    [DefaultValue(false)]
    public bool MyValue { get; set; }
}


public class TestSerializer
{
    public void Serialize()
    {
        XmlSerializer serializer = new XmlSerializer(typeof(MyType));

        using (TextWriter writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
        {
            serializer.Serialize(writer, new MyType());
        }
    }
}

我已经在考虑通过反射来做到这一点,但无法让它发挥作用。XmlAttributeOverrides似乎也没有帮助,但也许我还没有找到正确的方法。有任何想法吗?

标签: c#xmlxmlserializer

解决方案


XmlSerializerIgnoringDefaultValuesKey.Create这个答案Force XML serialization of XmlDefaultValue values主要是做你需要的,但是你已经通过 apply 覆盖了 XML 元素名称,MyValue据说[XmlElement(ElementName = "myValue", Form = XmlSchemaForm.Unqualified)]在那个答案中没有实现:

使用此 XmlAttributes 对象覆盖时,任何希望保留的属性都需要转移到此新对象中。

以下增强版本支持转移[XmlElement][XmlArray]覆盖[XmlArrayItem]属性:

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 AddOverride(XmlAttributeOverrides overrides, Type type, MemberInfo memberInfo)
    {
        var xmlElementAttr = memberInfo.GetCustomAttributes<XmlElementAttribute>();
        var xmlArrayAttr = memberInfo.GetCustomAttribute<XmlArrayAttribute>();
        var xmlArrayItemAttr = memberInfo.GetCustomAttributes<XmlArrayItemAttribute>();
        var attrs = new XmlAttributes 
        { 
            XmlDefaultValue = null,
            XmlArray = xmlArrayAttr,
        };
        foreach (var a in xmlElementAttr)
            attrs.XmlElements.Add(a);
        foreach (var a in xmlArrayItemAttr)
            attrs.XmlArrayItems.Add(a);
        overrides.Add(type, memberInfo.Name, attrs);
    }

    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;
        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 (Attribute.IsDefined(property, typeof(DefaultValueAttribute), true))
                {
                    withOverrides.Add(type);
                    AddOverride(overrides, type, property);
                }
        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 (Attribute.IsDefined(field, typeof(DefaultValueAttribute), true))
                {
                    withOverrides.Add(type);
                    AddOverride(overrides, type, field);
                }
        visited.Add(type);
        if (recurse)
        {
            var baseType = type.BaseType;
            if (baseType != type)
                CreateOverrideAttributes(baseType, recurse, overrides, visited, withOverrides);
        }
    }
}

并像这样使用它:

var serializer = XmlSerializerIgnoringDefaultValuesKey.Create(typeof(MyType), new[] { typeof(MyType) }, true).GetSerializer();          

using (TextWriter writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
{
    serializer.Serialize(writer, new MyType());
}           

演示小提琴在这里


推荐阅读