C# 默认情况下,如何使XmlSerializer生成属性而不是元素

C# 默认情况下,如何使XmlSerializer生成属性而不是元素,c#,.net,xml-serialization,xmlserializer,C#,.net,Xml Serialization,Xmlserializer,是否有一种方法可以使XmlSerializer将基本类成员(例如字符串属性)序列化为XML属性,而不是XML元素,而不必在每个属性声明前写入[xmltribute]? 也就是说,是否有一个全局开关告诉XmlSerializer将所有基本类成员序列化为XML属性 假设我们有以下类: public class Person { public string FirstName { ... } public string LastName {

是否有一种方法可以使
XmlSerializer
将基本类成员(例如字符串属性)序列化为XML属性,而不是XML元素,而不必在每个属性声明前写入
[xmltribute]
? 也就是说,是否有一个全局开关告诉
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"/>

再次强调:我想在没有
[xmldattribute]
的情况下(或者没有
xmldattributeoverrides
,这将是更多的工作)


一种可能的解决方案是使用通用的后处理步骤,应用XSLT转换将元素转换为属性。但是我想知道是否有更简单的解决方案。

实现这一点的一种方法是在实现接口的基类中实现序列化逻辑。然后,要序列化为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);
        }
    }
}
这里我们使用从当前对象获取属性列表,该对象的类型是基元或。然后,使用提供的对象将这些属性作为属性写入XML输出

要序列化的类只需从
XmlSerializableEntity
继承即可自动获得此行为:

[Serializable]
public class Foo : XmlSerializableEntity
{
    public int Bar { get; set; }
}

我认为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>


话虽如此,我想知道元素是否比属性慢,特别是当发送到另一行时。

Enrico的回答似乎是问题的解决方案,但它肯定比您正在避免的两个更复杂,也不太可取。也许您可以告诉我们为什么不能使用这些方法,以便我们知道您具体在避免什么?我需要与需要特定XML格式的外部系统交换数据。在该格式中,简单数据类型表示为属性,复杂数据类型(例如列表)表示为属性。数据模型有大约50个类和500个属性。我想避免在每个属性前面写[XmlAttribute]。@Kyle W在这种情况下,将通用XML序列化逻辑放在一个地方(如基类)肯定是有意义的,因为它保留了内容。@Enrico我不确定这算不算重复。。。至少不在于它与“干”的关系。fmunkert:老实说,我自己会把所有的标签都放在那里,或者写一些东西来解析东西,为我创建类。复制/粘贴XmlAttribute将是实际创建类的一小部分。但YMMV。此解决方案的缺点是,其他XML序列化属性(如[XmlIgnore])不再适用于从XmlSerializableEntity派生的类(除非我重新实现XmlSerializer在XmlSerializableEntity中执行的所有属性处理)。@fmunkert,您必须通过
XmlSerializableEntity
类中的反射来检查您自己。然而,在一个中心位置拥有这种专门的共享行为肯定会对未来的修改有所帮助。我接受这个解决方案,因为它回答了我的问题。但是,我将以不同的方式实现它,因为如果我仍然希望能够使用所有Xml*属性类,Enrico的解决方案将需要太多的工作。我正在考虑使用覆盖所有基本属性的反射动态生成XmlAttributeOverrides实例;或者,我也可以编写代码,使用CodeDom自动生成代理类。