XmlSerializer:将类属性序列化为自定义子元素的属性
本文关键字:属性 自定义 元素 序列化 XmlSerializer | 更新日期: 2023-09-27 18:22:33
我使用的是XmlSerializer
。我的班级:
[Serializable]
[XmlRoot(ElementName="MyClass")]
public class MyClass
{
public string Value;
}
我想序列化它,使Value
最终成为名为(例如)"Text"的子元素的属性。
期望结果:
<MyClass>
<Text Value="3"/>
</MyClass>
但是NOT(这将是将Value标记为XmlAttribute
的效果)
<MyClass Value="3">
</MyClass>
并且NOT(这将是将Value标记为XmlElement
的效果):
<MyClass>
<Value>3</Value>
</MyClass>
我该如何做到这一点?
我知道我可以将Value
的类型从字符串更改为另一个可序列化的自定义类
不幸的是,我有很多这样的属性,所以我需要创建几十个小类。
有什么更快的解决方案吗?
编辑:
回复您的意见:
不,并不是每个属性都必须序列化为名为"Text"的子元素。子元素的名称是唯一且明确的。
示例输出XML:
<visibility> <site visible="yes"/> <comparator visible="no"/> <expiration days="7"/> <comment>blahblahblah</comment> <visibility>
样品类别:
[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")
,但你会得到类似的结果:
<MyClass>
<Text>3</Text>
</MyClass>
编辑:另一个解决方案可能涉及XSLT转换:您可以使用.Net序列化生成xml,然后应用xml转换。
XslTransform myXslTransform = new XslTransform();
myXslTransform.Load(xsltDoc);
myXslTransform.Transform(sourceDoc, resultDoc);
我的例子的转换应该是这样的:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="MyClass">
<MyClass>
<Text>
<xsl:attribute name="Value">
<xsl:value-of select="Text"/>
</xsl:attribute>
</Text>
</MyClass>
</xsl:template>
</xsl:stylesheet>
对于这种灵活性,您应该真正考虑实现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<T>(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
以它喜欢的方式序列化我的类,然后只转换输出,将某些元素重新定位到嵌套位置。
类似的东西:
/// <remarks>
/// (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)
/// </remarks>
static XElement Reposition(XElement input, KeyValuePair<string, string[]>[] 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
属性:请参阅我问题中的示例代码)结合起来,并将此功能封装在我自己的序列化程序类中。
对这种方法有什么意见/见解吗?
我可以想到一个潜在的性能缺陷(在每次序列化后重写/重新分析XML),但生成的XML片段预计不会很大,因此这可能可以忽略不计。
在这一点上,反序列化的问题并不困扰我(反序列化已经实现,并且是通过XPath和一些实用程序方法"手动"完成的)。