2011-09-20 15 views
7

Tengo una aplicación C# .NET 3.5 donde me gustaría serializar una clase que contiene un List<> a XML. Mi clase es el siguiente:serializar una lista <> exportada como una colección <> a XML

[XmlRoot("Foo")] 
class Foo 
{ 
    private List<Bar> bar_ = new List<Bar>(); 

    private string something_ = "My String"; 

    [XmlElement("Something")] 
    public string Something { get { return something_; } } 

    [XmlElement("Bar")] 
    public ICollection<Bar> Bars 
    { 
     get { return bar_; } 
    } 
} 

Si lo pueblan la siguiente manera:

Bar b1 = new Bar(); 
// populate b1 with interesting data 
Bar b2 = new Bar(); 
// populate b2 with interesting data 

Foo f = new Foo(); 
f.Bars.Add(b1); 
f.Bars.Add(b2); 

Y luego serializarlo así:

using (System.IO.TextWriter textWriter = new System.IO.StreamWriter(@"C:\foo.xml")) 
{ 
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Foo)); 
    serializer.Serialize(textWriter, f); 
} 

consigo un archivo que tiene el siguiente aspecto:

<Foo> 
    <Something>My String</Something> 
</Foo> 

Pero, lo que quiero es X ML que tiene este aspecto:

<Foo> 
    <Something>My String</Something> 
    <Bar> 
     <!-- Data from first Bar --> 
    </Bar> 
    <Bar> 
     <!-- Data from second Bar --> 
    </Bar> 
</Foo> 

¿Qué tengo que hacer para conseguir la List<> para estar presente en el XML?

+0

no creo que pueda 'XmlSerialize' una interfaz. ¿Por qué quieres serializar como 'ICollection' de todos modos? Serialize como 'List ' y devuelva al consumidor un 'ICollection ' ... ??? – IAbstract

+0

@IAbstract - No estoy seguro de entender. ¿Quiere marcar la 'lista privada bar_' con la etiqueta' [XmlElement ("Bar")] '? Eso no cambia la salida. Además, la documentación 'XmlSerializer' sugiere que funciona con las interfaces' IEnumerable' y 'ICollection'. http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer%28v=VS.90%29.aspx – PaulH

+0

Creo que IAbstract lo tiene: no se puede serializar una interfaz. Por lo tanto, en su lugar debe cambiar Foo para que Bars sea una lista, no un ICollection –

Respuesta

2

El XmlSerializer requiere que las propiedades serializables tengan un colocador. Además de eso, el XmlSerializer no puede serializar las propiedades de la interfaz. El siguiente código funcionará:

[XmlElement("Bar")] 
public List<Bar> Bars 
{ 
    get { return bar_; } 
    set { throw new NotSupportedException("This property 'Bars' cannot be set. This property is readonly."); } 
} 

Si no te gusta esta solución (la excepción es un poco feo) entonces se podría aplicar IXmlSerializable y escribir su propia serialización personalizado.

Editar: Artur Mustafin es correcto, los miembros que implementen IEnumerable o ICollectionno necesita un regulador, tal como se explica en this msdn page:

XmlSerializer da un tratamiento especial a las clases que implementan IEnumerable o ICollection. Una clase que implemente IEnumerable debe implementar un método público Add que tome un solo parámetro. El parámetro del método Add debe ser del mismo tipo que el devuelto desde la propiedad Current en el valor devuelto desde GetEnumerator, o una de las bases de ese tipo. Una clase que implementa ICollection (como CollectionBase) además de IEnumerable debe tener una propiedad indexada pública Item (indizador en C#) que toma un entero, y debe tener una propiedad pública Count de tipo entero. El parámetro para el método Add debe ser del mismo tipo que el devuelto desde la propiedad Item, o una de las bases de ese tipo. Para las clases que implementan ICollection, los valores que se serializarán se recuperarán de la propiedad indexada Item, no llamando al GetEnumerator.

+2

Esta es la respuesta incorrecta y fea, esto está mal porque funciona sin setter, ver mi publicación –

+0

El XmlSerializer NO requiere que las propiedades serializables tengan un setter –

3

Dando una respuesta correcta, no tiene sentido crear setter feo en la propiedad pública List<T>, para lanzar una excepción.

Esto es porque List<> ya implementa ICollection<T> y proporciona el método con el vacío de firma Add(T object) que se utiliza por el mecanismo de serialización;

Usted sólo tiene que añadir el colocador a las propiedades públicas por entregas, y cambiar ICollection<T> a List<T>:

[XmlRoot("Foo")] 
public class Foo 
{ 
    private List<Bar> bar_ = new List<Bar>(); 

    [XmlElement("Something")] 
    public string Something { get; set; } 

    [XmlElement("Bar")] 
    public List<Bar> Bars { get { return bar_; } } 
} 

Usted recibirá una salida:

<?xml version="1.0" encoding="utf-8"?> 
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Something>My String</Something> 
    <Bar /> 
    <Bar /> 
</Foo> 

Además, es una Mejor idea para serializar xml en la memoria, para ver los resultados, o probarlo, de la siguiente manera:

static void Main(string[] args) 
{ 
    Bar b1 = new Bar(); 
    // populate b1 with interesting data 
    Bar b2 = new Bar(); 
    // populate b2 with interesting data 

    Foo f = new Foo(); 
    f.Bars.Add(b1); 
    f.Bars.Add(b2); 
    f.Something = "My String"; 

    using (MemoryStream ms = new MemoryStream()) 
    using (System.IO.TextWriter textWriter = new System.IO.StreamWriter(ms)) 
    { 
     System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Foo)); 
     serializer.Serialize(textWriter, f); 
     string text = Encoding.UTF8.GetString(ms.ToArray()); 
     Console.WriteLine(text); 
    } 

    Console.ReadKey(false); 
} 

para serializar el uso de interfaces, utilice mi proyecto XmlSerialization

Cuestiones relacionadas