Как сериализовать свойства с помощью DefaultValueAttribute с помощью XmlSerializer?

Я использую XmlSerializer для сериализации объектов С# в 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 сериализовать эти свойства без изменения исходного кода?


person Mykhailo Seniutovych    schedule 17.07.2018    source источник


Ответы (1)


Вы можете сделать это, передав экземпляр 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();
    }
}

Обновление: приведенное выше означает, что вы должны снова предоставить другие несвязанные атрибуты для каждого свойства, которое вы изменяете. Это немного хлопотно, если у вас много свойств и вы просто хотите удалить значения по умолчанию для всех из них. Приведенный ниже класс можно использовать для сохранения других настраиваемых атрибутов при удалении только одного типа атрибута. При необходимости его можно расширить, чтобы сделать это только для определенных свойств и т. д.

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);
person steve16351    schedule 17.07.2018
comment
Спасибо за ответ! Если я переопределю это так attributeOverrides.Add(typeof(Person), "Name", new XmlAttributes() { XmlDefaultValue = null });, то все XmlAttributes будут переопределены, в моем примере [System.Xml.Serialization.XmlAttribute()] больше не будет. Есть ли способ переопределить только DefaultValue и оставить все остальные атрибуты нетронутыми? - person Mykhailo Seniutovych; 17.07.2018
comment
Насколько я вижу, нет, хотя вы также можете вручную установить другие атрибуты для каждого изменяемого свойства, чтобы восстановить их. Я добавил некоторый код, который может автоматизировать создание XmlAttributeOverrides для копирования существующих и последующей настройки, в моем примере предполагается, что вы хотите удалить DefaultValueAttribute для всех свойств в данном классе. - person steve16351; 17.07.2018