如何使用 XmlSerializer 使用 DefaultValueAttribute 序列化属性?

How to serialize properties with DefaultValueAttribute using XmlSerializer?

我正在使用 XmlSerializer 将 C# 对象序列化为 XML。我在 类 的某些属性上有 DefaultValueAttribute 我正在尝试序列化,当我尝试序列化它们时,似乎 XmlSerializer 不包含 xml 中的值,如果它等于默认值。 看这个例子:

using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace Test
{
    public class Person
    {
        [System.Xml.Serialization.XmlAttribute()]
        [System.ComponentModel.DefaultValue("John")]
        public string Name { get; set; }
    }

    public static class Test
    {
        public static void Main()
        {
            var serializer = new XmlSerializer(typeof(Person));
            var person = new Person { Name = "John" };

            using (var sw = new StringWriter())
            {
                using (var writer = XmlWriter.Create(sw))
                {
                    serializer.Serialize(writer, person);
                    var xml = sw.ToString();
                }
            }
        }
    }
}

它将产生以下内容xml(注意名称属性不可用):

<?xml version="1.0" encoding="utf-16"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />

我无法修改 类 的源代码,所以我无法删除 DefaultValueAttribute。有没有办法在不更改源代码的情况下使 XmlSerializer 序列化此属性?

您可以在创建时将 XmlAttributeOverrides 实例传递给 XmlSerializer 来实现,如下所示。您可以使用此方法将默认值更改为其他值,或将其设置为 null 以有效删除它。

XmlAttributeOverrides attributeOverrides = new XmlAttributeOverrides();

var attributes = new XmlAttributes()
{
    XmlDefaultValue = null,
    XmlAttribute = new XmlAttributeAttribute()
};

attributeOverrides.Add(typeof(Person), "Name", attributes);

var serializer = new XmlSerializer(typeof(Person), attributeOverrides);
var person = new Person { Name = "John" };

using (var sw = new StringWriter())
{
    using (var writer = XmlWriter.Create(sw))
    {
        serializer.Serialize(writer, person);
        var xml = sw.ToString();
    }
}

更新:以上意味着您必须在每个要更改的 属性 上再次提供其他不相关的属性。如果您有很多属性并且只想删除所有属性的默认值,这会有点麻烦。下面的 class 可用于保留其他自定义属性,同时仅删除一种类型的属性。如果需要只对某些属性等进行扩展,它可以进一步扩展。

public class XmlAttributeOverrideGenerator<T>
{
    private static XmlAttributeOverrides _overrides;
    private static Type[] _ignoreAttributes = new Type[] { typeof(DefaultValueAttribute) };

    static XmlAttributeOverrideGenerator()
    {
        _overrides = Generate();
    }

    public static XmlAttributeOverrides Get()
    {
        return _overrides;
    }

    private static XmlAttributeOverrides Generate()
    {
        var xmlAttributeOverrides = new XmlAttributeOverrides();

        Type targetType = typeof(T);
        foreach (var property in targetType.GetProperties())
        {
            XmlAttributes propertyAttributes = new XmlAttributes(new CustomAttribProvider(property, _ignoreAttributes));
            xmlAttributeOverrides.Add(targetType, property.Name, propertyAttributes);
        }

        return xmlAttributeOverrides;
    }

    public class CustomAttribProvider : ICustomAttributeProvider
    {
        private PropertyInfo _prop = null;
        private Type[] _ignoreTypes = null;            

        public CustomAttribProvider(PropertyInfo property, params Type[] ignoreTypes)
        {
            _ignoreTypes = ignoreTypes;
            _prop = property;
        }

        public object[] GetCustomAttributes(bool inherit)
        {
            var attribs = _prop.GetCustomAttributes(inherit);
            if (_ignoreTypes == null) return attribs;
            return attribs.Where(attrib => IsAllowedType(attrib)).ToArray();
        }

        private bool IsAllowedType(object attribute)
        {
            if (_ignoreTypes == null) return true;
            foreach (Type type in _ignoreTypes)
                if (attribute.GetType() == type)
                    return false;

            return true;
        }

        public object[] GetCustomAttributes(Type attributeType, bool inherit)
        {
            throw new NotImplementedException();
        }

        public bool IsDefined(Type attributeType, bool inherit)
        {
            throw new NotImplementedException();
        }
    }
}

用法:

XmlAttributeOverrides attributeOverrides = XmlAttributeOverrideGenerator<Person>.Get();            
var serializer = new XmlSerializer(typeof(Person), attributeOverrides);

我没有足够的声誉来评论 steve16351 的回答。但我觉得我对他的代码做了一点改进。

我的用例涉及通过 Microsoft 提供的 XSD.exe 生成的 XML 架构文件,然后从架构文件生成 class。所以 class 上有架构中的所有 DefaultValue 标签。还创建了许多嵌套类型。因此,为了使我的代码简洁,我修改了 'Generate' 方法以获取 top-level class 及其下方的所有方法的覆盖。然后所有内容都在单个 XmlAttributesOverrides 对象中返回。

static XmlAttributeOverrideGenerator()
{
    _overrides = Generate(typeof(T), new XmlAttributeOverrides());
}

private static XmlAttributeOverrides Generate(Type targetType, XmlAttributeOverrides Overrides)
{
    foreach (var property in targetType.GetProperties())
    {
        if (property.CustomAttributes.Any( CA => CA.AttributeType == typeof(DefaultValueAttribute)))
        {
            XmlAttributes propertyAttributes = new XmlAttributes(new CustomAttribProvider(property, _ignoreAttributes));
            Overrides.Add(targetType, property.Name, propertyAttributes);
        }
        else
        {
            Type propType = property.PropertyType;
            if (propType.IsClass && !propType.IsValueType && !propType.IsEnum && !propType.IsPrimitive && !propType.IsSealed)  // Check if this is a custom class
            {
                //If this is a nested class or other class type, generate its overrides as well
                Generate(propType, Overrides);
            }
        }
    }

    return Overrides;
}