XmlSerializer: сериализация свойства classа как атрибута пользовательского подэлемента

Я использую XmlSerializer . Мои занятия:

 [Serializable] [XmlRoot(ElementName="MyClass")] public class MyClass { public string Value; } 

Я хотел бы сериализовать его так, чтобы Value заканчивалось как атрибут подэлемента, названного (например) «Текст».

Желаемый результат:

    

Но НЕ (что было бы результатом маркировки значения как XmlAttribute )

   

И NOT (что было бы следствием маркировки Value как XmlElement ):

  3  

Как мне это достичь?

Я знаю, что я могу изменить тип Value из строки в другой сериализуемый пользовательский class.

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

Есть ли более быстрое решение?


РЕДАКТИРОВАТЬ:

В ответ на ваши комментарии:

  • Нет, не каждое свойство должно быть сериализовано в подэлемент под названием «Текст». Имя субэлемента уникально и недвусмысленно.

  • Пример вывода XML:

         blahblahblah  
  • Класс образца:

!

 [XmlRoot(ElementName="Visibility")] public class Visibility { [XPath("/site@visible")] // if only this was possible! public string OnSite { get { return SiteVisible ? "yes" : "no"; } } [XPath("/comparator@visible")] // as above... public string InComparator { get { return ComparatorVisible ? "yes" : "no"; } } [XmlIgnore] public bool SiteVisible; [XmlIgnore] public bool ComparatorVisible; [XPath("/expiration@days")] // as above... public int ExpiresAfterDays; [XmlElement("comment")] // this is easy public string Comment; } 

Не меняя тип Value я думаю, что это невозможно. Вы можете добавить атрибут XmlElement(ElementName="Text") в Value но вы получите результат, подобный этому:

  3  

Отредактировано: Другое решение может включать преобразование XSLT: вы можете сгенерировать xml с использованием сериализации .Net и после применения преобразования xml.

 XslTransform myXslTransform = new XslTransform(); myXslTransform.Load(xsltDoc); myXslTransform.Transform(sourceDoc, resultDoc); 

Преобразование моего примера должно быть примерно таким:

                 

Для такого рода гибкости вы должны действительно думать об осуществлении IXmlSerializable поскольку это дает вам гораздо больше контроля:

 [XmlRoot("visibility")] public class Visibility : IXmlSerializable { public string Site; public string Comparator; public int Expiration; public string Comment; public XmlSchema GetSchema() { throw new NotImplementedException(); } public void ReadXml(XmlReader reader) { // implement me if you want to deserialize too. throw new NotImplementedException(); } public void WriteXml(XmlWriter writer) { WriteProperty(writer, "site", "visible", Site); WriteProperty(writer, "comparator ", "visible", Comparator); WriteProperty(writer, "expiration ", "days", Expiration); if (!string.IsNullOrEmpty(Comment)) { writer.WriteElementString("comment", Comment); } } private void WriteProperty(XmlWriter writer, string elementName, string attibuteName, T value) { if (value != null) { writer.WriteStartElement(elementName); writer.WriteAttributeString(attibuteName, value.ToString()); writer.WriteEndElement(); } } } 

Очевидно, что здесь немного ручного труда, но это позволяет вам хранить весь код сериализации в одном месте, вместо того, чтобы иметь распространение меньших classов.

В приведенном выше примере реализована только сериализация – вам нужно будет написать эквивалентную реализацию десериализации, если вам нужно десериализовать из xml в ваш тип.

Спасибо за ответы на все вопросы. Жаль, что библиотека .NET XmlSerialization не позволяет этого (я думаю, это должно быть!). Я ищу решение как можно более общее.

Самый лучший из них, который я мог бы придумать (лучше всего, учитывая критерии максимальной универсальности, будучи достаточно быстрым для реализации) позволяет XmlSerializer сериализовать мой class так, как ему нравится, а затем просто преобразовать вывод, переместив определенные элементы во вложенные места.

Что-то вроде того:

  ///  /// (angle brackets replaced with round ones to avoid confusing the XML-based documentation comments format) /// /// Let input XML be: /// /// (root) /// (days)3(/days) /// (/root) /// /// Calling Reposition on this input with mappings argument being: /// (key) "days" /// (value) { "time", "days" } /// /// Returns: /// (root) /// (time days="3" /) /// (/root) ///  static XElement Reposition(XElement input, KeyValuePair[] mappings) { var result = new XElement(input); foreach (var mapping in mappings) { var element = result.Element(mapping.Key); if (element == null) { continue; } var value = element.Value; element.Remove(); var insertAt = result; foreach (var breadcrumb in mapping.Value) { if (breadcrumb == mapping.Value.Last()) { insertAt.Add(new XAttribute(breadcrumb, value)); } else { insertAt.Add(new XElement(breadcrumb)); insertAt = insertAt.Element(breadcrumb); } } } return result; } 

Я думаю, что я бы объединил его с пользовательским атрибутом (вроде похожего на атрибут XPath который, как я хотел бы, существовал бы: см. Пример кода в моем вопросе), и включите эту функциональность в собственный class сериализатора.

Любые комментарии / идеи по этому подходу?

Я могу придумать потенциальный недостаток производительности (переписывание / репарация XML после каждой сериализации), но полученные fragmentы XML не должны быть большого размера, поэтому это, вероятно, незначительно.

Вопрос о десериализации не беспокоит меня в этот момент (десериализация уже реализована и выполняется «вручную», XPath и некоторые утилиты).