Как заставить 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-преобразование для преобразования элементов в атрибуты. Но мне интересно, есть ли более простое решение.


person Community    schedule 03.08.2011    source источник
comment
Ответ Энрико кажется решением вопроса, но он определенно сложнее и менее желателен, чем те два, которых вы избегаете. Может быть, вы расскажете нам, почему вы не можете использовать эти методы, чтобы мы знали, чего конкретно вы избегаете?   -  person Kyle W    schedule 04.08.2011
comment
Мне нужно обмениваться данными с внешней системой, для которой требуется определенный формат XML. В этом формате простые типы данных представлены как атрибуты, сложные типы данных (например, списки) представлены как атрибуты. Модель данных имеет около 50 классов и 500 атрибутов. Я хочу избежать необходимости писать [XmlAttribute] перед каждым атрибутом.   -  person    schedule 04.08.2011
comment
@Kyle W В этом случае наличие общей логики сериализации XML в одном месте (например, в базовом классе) определенно имело бы смысл, поскольку оно сохраняет вещи СУХОЙ.   -  person Enrico Campidoglio    schedule 04.08.2011
comment
@ Энрико, я не уверен, что это считается повторением ... по крайней мере, не в том, как это связано с DRY. fmunkert: Честно говоря, я бы сам расставил там все теги или написал бы что-нибудь, что могло бы анализировать вещи и создавать классы для меня. Копирование/вставка XmlAttribute будет небольшой частью фактического создания классов. Но ЮММВ.   -  person Kyle W    schedule 04.08.2011


Ответы (2)


Один из способов добиться этого — реализовать логику сериализации в базовом классе, который реализует интерфейс 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; }
}
person Enrico Campidoglio    schedule 03.08.2011
comment
Недостатком этого решения является то, что другие атрибуты сериализации XML, такие как [XmlIgnore], больше не будут работать в классах, производных от XmlSerializableEntity (если только я не переосуществлю всю обработку атрибутов, которую XmlSerializer выполняет в XmlSerializableEntity). - person ; 04.08.2011
comment
@fmunkert Действительно, вам нужно будет проверить XmlIgnoreAttribute самостоятельно с помощью Reflection в классе XmlSerializableEntity. Однако наличие такого специализированного общего поведения в центральном месте определенно принесет пользу будущим модификациям. - person Enrico Campidoglio; 04.08.2011
comment
Я принимаю это решение, потому что оно отвечает на мой вопрос. Однако я реализую его по-другому, потому что решение Энрико потребует слишком много работы, если я все еще хочу иметь возможность использовать все классы Xml*Attribute. Я думаю о динамическом создании экземпляра XmlAttributeOverrides с использованием отражения, которое переопределяет все примитивные свойства; в качестве альтернативы я мог бы написать код для автоматического создания прокси-классов с помощью CodeDom. - person ; 05.08.2011

Я думаю, что 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>

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

person toddmo    schedule 10.01.2012