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