在 C# 中删除 XML 序列化的 DefaultValue 属性

Posted

技术标签:

【中文标题】在 C# 中删除 XML 序列化的 DefaultValue 属性【英文标题】:Remove DefaultValue attribute for XML serialization in C# 【发布时间】:2020-12-26 03:34:29 【问题描述】:

我正在将一个对象序列化为 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 似乎也没有帮助,但也许我还没有找到正确的方法。有什么想法吗?

【问题讨论】:

你看到Force XML serialization of XmlDefaultValue values了吗? This answer specifically 显示如何配置 XmlAttributeOverrides 以禁用 [DefaultValue(false)] 属性。 【参考方案1】:

XmlSerializerIgnoringDefaultValuesKey.Create 从 this answer 到 Force XML serialization of XmlDefaultValue values 主要满足您的需求,但是您通过应用 [XmlElement(ElementName = "myValue", Form = XmlSchemaForm.Unqualified)] 覆盖了 MyValue 的 XML 元素名称,据说是未在该答案中实施:

使用此 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());
           

演示小提琴here.

【讨论】:

以上是关于在 C# 中删除 XML 序列化的 DefaultValue 属性的主要内容,如果未能解决你的问题,请参考以下文章

在C#中序列化和反序列化之间保留xml元素的顺序

如何反序列化 C# 中只有属性的 xml 元素?

在 C# 中使用派生类从 XML 反序列化对象

XML反序列化在c#中返回0结果

C# Restful WCF 服务。无法在帖子正文中反序列化 XML

C# 在忽略命名空间的同时反序列化 xml