c# .net xml-serialization xmlinclude

c# - Cómo agregar dinámicamente el atributo XmlInclude



.net xml-serialization (4)

Tengo las siguientes clases

[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 intento ejecutar el código, recibí una InvalidOperationException en la última línea que dice que

El tipo XmlTest.C no fue esperado. Utilice el atributo XmlInclude o SoapInclude para especificar tipos que no se conocen de forma estática.

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

Probé también con

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

antes de

var type = typeof (AList);

pero no sirve de nada. Todavía está dando la misma excepción.

¿Alguien tiene alguna idea de cómo lograrlo?


Dos opciones; El más simple (pero que da un 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>

El 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, debe almacenar en caché y reutilizar la instancia de ser ; De lo contrario, sufrirá una hemorragia en la memoria de una compilación dinámica.


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


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


Sobre la base de la primera respuesta de Marc (solo tengo que leer, así que no necesito evitar la salida extraña), uso una matriz de tipos más dinámica / genérica para tener en cuenta los tipos desconocidos, inspirados en este proyecto de 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); }

(Probablemente se podría hacer que sea más eficiente, por ejemplo, usar una matriz de tipos estática o de solo lectura en lugar de una variable local. Eso evitaría el uso repetitivo de Reflexión. Inicialícese, para saber si eso podría causarle problemas. Mi uso no es mucho, para tomarme el tiempo de investigar todo esto, así que solo uso la misma Reflexión varias veces.