2011-08-03 11 views
14

¿Hay una manera de hacer que XmlSerializer para serializar miembros de la clase primitivos (por ejemplo, las propiedades de cuerda) como atributos XML, no como elementos XML, sin tener que escribir [XmlAttribute] delante de cada declaración de propiedad? Es decir ¿Existe un cambio global que indique XmlSerializer serializar todos los miembros de la clase primitiva como atributos XML?Cómo hacer que XmlSerializer para generar atributos en lugar de elementos por defecto

Supongamos que tenemos la siguiente clase:

public class Person 
{ 
    public string FirstName 
    { 
     ... 
    } 

    public string LastName 
    { 
     ... 
    } 
} 

Entonces XmlSerializer genera el código por defecto:

<Person> 
    <FirstName>John</FirstName> 
    <LastName>Doe</LastName> 
</Person> 

Lo que quiero, sin embargo, es este código:

<Person FirstName="John" LastName="Doe"/> 

De nuevo: quiero hacer eso sin [XmlAttribute] (o sin XmlAttributeOverrides, que sería aún más trabajo).

Una posible solución sería utilizar un paso genérico de posprocesamiento que aplica una transformación XSLT para convertir elementos en atributos. Pero me pregunto si hay una solución más simple.

+0

La respuesta de Enrico parece ser una solución a la pregunta, pero ciertamente es más complicada y menos deseable que las dos que está evitando. Quizás puedas decirnos por qué no puedes usar esos métodos, para que sepamos qué, específicamente, estás evitando. –

+0

Necesito intercambiar datos con un sytsem externo que requiera un formato XML específico. En ese formato, los tipos de datos simples se representan como atributos, los tipos de datos complejos (por ejemplo, listas) se representan como atributos.El modelo de datos tiene alrededor de 50 clases y 500 atributos. Quiero evitar que tenga que escribir [XmlAttribute] delante de cada atributo. –

+0

@Kyle W En este caso, tener la lógica de serialización XML común en un solo lugar (como una clase base) definitivamente tendría sentido, ya que mantiene las cosas [DRY] (http://en.wikipedia.org/wiki/Don't_repeat_yourself) –

Respuesta

4

Una forma de lograrlo es implementar la lógica de la serialización en una clase base que implementa la interfazIXmlSerializable. Las clases que se serializarán a XML, tendrían que derivarse de esta clase base para obtener la funcionalidad.

He aquí un ejemplo

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); 
     } 
    } 
} 

Aquí estamos utilizando Reflection para obtener una lista de propiedades del objeto actual, cuyo tipo es una primitiva o una String. Estas propiedades se escriben en la salida XML como atributos usando el objeto proporcionado XmlWriter.

Las clases para ser serializado simplemente tendría que heredar de XmlSerializableEntity para obtener automáticamente este comportamiento:

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

La desventaja de esta solución es que otros atributos de serialización XML como [XmlIgnore] ya no funcionarían en las clases derivadas de XmlSerializableEntity (a menos que reimplantara todo el procesamiento de Atributo que XmlSerializer hace en XmlSerializableEntity). –

+0

@fmunkert De hecho, usted debería verificar el [XmlIgnoreAttribute] (http://msdn.microsoft.com/en-us/library/system.xml.serviceization.xmlignoreattribute.aspx) usted mismo a través de Reflection en el 'XmlSerializableEntity' clase. Sin embargo, tener este tipo de comportamiento compartido especializado en una ubicación central definitivamente beneficiaría las modificaciones futuras. –

+0

Estoy aceptando esta solución, porque responde mi pregunta. Sin embargo, lo implementaré de manera diferente porque la solución de Enrico requeriría demasiado trabajo si aún quisiera poder usar todas las clases de atributos Xml *. Estoy pensando en generar una instancia de XmlAttributeOverrides dinámicamente utilizando reflection que anule todas las propiedades primitivas; o alternativa, podría escribir código para autogenerar clases proxy usando CodeDom. –

0

creo XSLT es la forma más estable, fácil de mantener y elegante para ir. No requiere volver a basar todo, se basa en la serialización ya probada, permite atributos personalizados xml en la clase, y se puede hacer en la memoria con una transformación compilada.

Aquí es un poco de XSLT genérico que debe hacerlo:

<?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> 

Una vez dicho esto, me pregunto si los elementos son más lentos que los atributos, especialmente cuando se envía a través de la línea.

Cuestiones relacionadas