XmlSerializer:将类属性序列化为自定义子元素的属性

我正在使用XmlSerializer 。 我的课:

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

我想序列化它,以便Value最终作为名为(例如)“Text”的子元素的属性。

期望的结果:

    

但是NOT (这会将Value标记为XmlAttribute的效果)

   

不是 (这将标记Value作为XmlElement的效果):

  3  

我该如何实现这一目标?

我知道我可以将Value的类型从string更改为另一个可序列化的自定义类。

不幸的是,我有很多这样的属性,所以我需要创建几十个小类。

有没有更快的解决方案?


编辑:

回应你的意见:

  • 不,并非每个属性都必须序列化为名为“Text”的子元素。 Subelement的名称是独特且明确的。

  • 示例输出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类型的情况下,我认为这是不可能的。 您可以在Value上添加属性XmlElement(ElementName="Text") ,但您将获得与此类似的结果:

  3  

编辑:另一个解决方案可能涉及XSLT转换:您可以使用.Net序列化生成xml并应用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(); } } } 

显然,这里有一些手动工作,但它确实允许您将所有序列化代码保存在一个地方,而不是增加较小的类。

上面的示例仅实现序列化 – 如果需要从xml反序列化到类型,则需要编写等效的反序列化实现。

感谢所有的答案。 遗憾的是.NET XmlSerialization库不允许这样做(我认为它应该!)。 我正在寻找尽可能通用的解决方案。

我能想出的最好的一个(考虑到最大通用性的标准,同时合理地快速实现)是让XmlSerializer自己喜欢的方式序列化我的类,然后只需转换输出,将某些元素重新定位到嵌套位置。

像这样的东西:

  ///  /// (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属性类似:请参阅我的问题中的示例代码),并在我自己的序列化程序类中包装此function。

有关此方法的任何意见/见解?

我可以想到一个潜在的性能缺陷(在每次序列化后重写/重新表示XML),但是生成的XML片段预计不会很大,所以这可能是微不足道的。

反序列化的问题在这一点上并没有打扰我(反序列化已经实现并且通过XPath和一些实用方法完全“手动”完成)。