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

Myk*_*ych 4 c# xml attributes xmlwriter 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();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它将生成以下 xml(注意 name 属性不可用):

<?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" />
Run Code Online (Sandbox Code Playgroud)

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

ste*_*351 5

您可以通过在创建实例时将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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

XmlAttributeOverrides attributeOverrides = XmlAttributeOverrideGenerator<Person>.Get();            
var serializer = new XmlSerializer(typeof(Person), attributeOverrides);
Run Code Online (Sandbox Code Playgroud)