2010-04-22 15 views
23

tengo las siguientes clasesCómo agregar atributos XmlInclude dinámicamente

[XmlRoot] 
public class AList 
{ 
    public List<B> ListOfBs {get; set;} 
} 

public class B 
{ 
    public string BaseProperty {get; set;} 
} 

public class C : B 
{ 
    public string SomeProperty {get; set;} 
} 

public class Main 
{ 
    public static void Main(string[] args) 
    { 
     var aList = new AList(); 
     aList.ListOfBs = new List<B>(); 
     var c = new C { BaseProperty = "Base", SomeProperty = "Some" }; 
     aList.ListOfBs.Add(c); 

     var type = typeof (AList); 
     var serializer = new XmlSerializer(type); 
     TextWriter w = new StringWriter(); 
     serializer.Serialize(w, aList); 
    }  
} 

Ahora, cuando trato de ejecutar el código que tengo una InvalidOperationException en la última línea diciendo que

El tipo XmlTest.C no se esperaba . Utilice el atributo XmlInclude o SoapInclude para especificar los tipos que no se conocen estáticamente.

Sé que al agregar un atributo [XmlInclude (typeof (C))] con [XmlRoot] se resolvería el problema. Pero quiero lograrlo dinámicamente. Porque en mi clase de proyecto C no se conoce antes de la carga. La clase C se está cargando como un complemento, por lo que no me es posible agregar el atributo XmlInclude allí.

me trataron también con

TypeDescriptor.AddAttributes(typeof(AList), new[] { new XmlIncludeAttribute(c.GetType()) }); 

antes

var type = typeof (AList); 

pero sin uso. Todavía está dando la misma excepción.

¿Alguien tiene alguna idea sobre cómo lograrlo?

Respuesta

28

Dos opciones; el más simple (pero dando xml impar) es:

XmlSerializer ser = new XmlSerializer(typeof(AList), 
    new Type[] {typeof(B), typeof(C)}); 

Con salida de ejemplo:

<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <ListOfBs> 
    <B /> 
    <B xsi:type="C" /> 
    </ListOfBs> 
</AList> 

La más elegante es:

XmlAttributeOverrides aor = new XmlAttributeOverrides(); 
XmlAttributes listAttribs = new XmlAttributes(); 
listAttribs.XmlElements.Add(new XmlElementAttribute("b", typeof(B))); 
listAttribs.XmlElements.Add(new XmlElementAttribute("c", typeof(C))); 
aor.Add(typeof(AList), "ListOfBs", listAttribs); 

XmlSerializer ser = new XmlSerializer(typeof(AList), aor); 

Con salida de ejemplo:

<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <b /> 
    <c /> 
</AList> 

En cualquier caso, usted debe caché y volver a usar la instancia ser; de lo contrario, se producirá una hemorragia en la memoria de la compilación dinámica.

+1

He intentado esto pero me está dando esta siguiente excepción "Los atributos XmlRoot y XmlType no se pueden especificar para el tipo XmlTest.AList." ¿Alguna idea de por qué viene y cómo resolverla? –

+0

@Anindya - eso es extraño; el ejemplo "tal como es" funciona bien para mí. ¿Qué versión de framework estás usando? –

+0

@Anindya - He intentado con 4.0 y 2.0 (que cubre 3.0 y 3.5) y no puedo reproducir. ¿Puedes dar una descripción más completa de esto? –

2

Eche un vistazo a la documentación de XmlSerializer. Hay un constructor que espera tipos conocidos como el segundo parámetro. Eso debería funcionar bien para su caso de uso.

0

No creo que los atributos puedan aplicarse en tiempo de ejecución, ya que se utilizan para crear metadatos en el código CIL.

6

Basándome en la primera respuesta de Marc (solo tengo que leer, así que no necesito evitar la salida extraña), utilizo una matriz de tipos más dinámica/genérica para dar cuenta de tipos desconocidos, inspirada en esta codeproject.

public static XmlSerializer GetSerializer() 
    { 
     var lListOfBs = (from lAssembly in AppDomain.CurrentDomain.GetAssemblies() 
          from lType in lAssembly.GetTypes() 
          where typeof(B).IsAssignableFrom(lType) 
          select lType).ToArray(); 
     return new XmlSerializer(typeof(AList), lListOfBs); 
    } 

(Posiblemente se podría hacerlo más eficiente, por ejemplo, utilizando un estático o de sólo lectura de tipo de matriz en lugar de una variable local. Con ello se evitaría repetidamente utilizando la reflexión. Pero no saben lo suficiente acerca de cuándo los ensamblajes se cargan y las clases y las propiedades se inicializan, para saber si eso podría meterlo en problemas. Mi uso no es mucho, tomarme el tiempo para investigar todo esto, así que utilizo la misma Reflexión varias veces.)

+0

funcionó como un amuleto, gracias (y orujo) mucho – Tiax

Cuestiones relacionadas