Как заставить XmlSerializer генерировать атрибуты вместо элементов по умолчанию

Есть ли способ заставить XmlSerializer сериализовать примитивные члены класса (например, свойства строки) как атрибуты XML, а не как элементы XML, без, чтобы написать [XmlAttribute] перед каждым свойством декларация? То есть есть глобальный переключатель, который сообщает XmlSerializer сериализовать все примитивные члены класса как атрибуты XML?

Предположим, что мы имеем следующий класс:

public class Person
{
    public string FirstName
    {
       ...
    }

    public string LastName
    {
       ...
    }
}

Затем XmlSerializer генерирует этот код по умолчанию:

<Person>
    <FirstName>John</FirstName>
    <LastName>Doe</LastName>
</Person>

Я хочу, однако, этот код:

<Person FirstName="John" LastName="Doe"/>

Опять же: я хочу сделать это без [XmlAttribute] (или без XmlAttributeOverrides, что будет еще больше работать).

Одним из возможных решений было бы использовать общий шаг постпроцессинга, который применяет XSLT-преобразование для преобразования элементов в атрибуты. Но мне интересно, есть ли более простое решение.

Ответы

Ответ 1

Один из способов достижения этой цели - реализовать логику сериализации в базовом классе, который реализует IXmlSerializable интерфейс. Классы, которые должны быть сериализованы в XML, затем должны были бы получить этот базовый класс, чтобы получить функциональность.

Здесь пример

public class XmlSerializableEntity : IXmlSerializable
{
    public XmlSchema GetSchema()
    {
        // Implementation omitted for clarity
    }

    public void ReadXml(XmlReader reader)
    {
        // Implementation omitted for clarity
    }

    public void WriteXml(XmlWriter writer)
    {
        var properties = from property in this.GetType().GetProperties()
                         where property.PropertyType.IsPrimitive ||
                               property.PropertyType == typeof(string)
                         select property;

        foreach (var property in properties)
        {
            var name = property.Name;
            var value = property.GetValue(this, null).ToString();
            writer.WriteAttributeString(name, value);
        }
    }
}

Здесь мы используем Reflection, чтобы получить список свойств текущего объекта, тип которого является примитивным или String. Эти свойства затем записываются в вывод XML как атрибуты, используя предоставленный объект XmlWriter.

Для сериализации классов просто нужно наследовать от XmlSerializableEntity, чтобы автоматически получить это поведение:

[Serializable]
public class Foo : XmlSerializableEntity
{
    public int Bar { get; set; }
}

Ответ 2

Я думаю, что Xslt является самым стабильным, простым в обслуживании и элегантным способом. Это не требует повторного базирования всего, опирается на уже протестированную сериализацию, позволяет использовать XML-атрибуты в классе, и может выполняться в памяти с помощью скомпилированного преобразования.

Вот несколько общих xslt, которые должны это сделать:

<?xml version=’1.0′ encoding=’utf-8′?>
<xsl:stylesheet version=’1.0′ xmlns:xsl=’http://www.w3.org/1999/XSL/Transform’ xmlns:msxsl=’urn:schemas-microsoft-com:xslt’ exclude-result-prefixes=’msxsl’>
<xsl:template match=’*'>
<xsl:copy>
<xsl:for-each select=’@*|*[not(* or @*)]‘>
<xsl:attribute name=’{name(.)}’><xsl:value-of select=’.'/>
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates select=’*[* or @*]|text()’/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Сказав это, я задаюсь вопросом, являются ли элементы медленнее, чем атрибуты, особенно когда они отправляются по линии.