2009-03-20 15 views
6

genérico # La configuración:c heredar recogida y serialización

class Item 
{ 
    private int _value; 

    public Item() 
    { 
     _value = 0; 
    } 

    public int Value { get { return _value; } set { _value = value; } } 
} 

class ItemCollection : Collection<Item> 
{ 
    private string _name; 

    public ItemCollection() 
    { 
     _name = string.Empty; 
    } 

    public string Name { get {return _name;} set {_name = value;} } 
} 

Ahora, al intentar serializar utilizando el siguiente fragmento de código:

ItemCollection items = new ItemCollection(); 

... 

XmlSerializer serializer = new XmlSerializer(typeof(ItemCollection)); 
using (FileStream f = File.Create(fileName)) 
    serializer.Serialize(f, items); 

Al mirar el XML resultante veo que el ItemCollection.Name el valor no está allí!

Creo que lo que puede estar ocurriendo es que el serializador ve el tipo ItemCollection como una simple colección ignorando así cualesquiera otras propiedades añadidas ...

¿Hay alguien de haber encontrado un problema tal y encontrado una solución?

Saludos,

Stecy

Respuesta

12

Este comportamiento es "Por diseño". Cuando se deriva de una clase de colección, Xml Seralizier solo serializará los elementos de la colección. Para solucionar este problema, debe crear una clase que encapsule la colección y el nombre y que se serialice.

class Wrapper 
{ 
    private Collection<Item> _items; 
    private string _name; 

    public Collection<Item> Items { get {return _items; } set { _items = value; } } 
    public string Name { get { return _name; } set { _name = value; } } 
} 

Una discusión detallada está disponible aquí: http://blogs.vertigo.com/personal/chris/Blog/archive/2008/02/01/xml-serializing-a-derived-collection.aspx

+0

+1; tenga en cuenta también que la mayoría de los marcos de enlace de datos también comparten el comportamiento. Simplemente no es una buena idea que las colecciones tengan propiedades; las colecciones tienen artículos (solo): ese es su trabajo. –

+0

Bien, ahora necesito ajustar varias clases derivadas de colecciones ... Me preocupa que pueda complicar el diagrama de clases ... –

4

XmlSerializer es malo. Dicho esto, cualquier objeto que implemente IEnumerable se serializará como una simple colección, ignorando cualquier propiedad adicional que haya agregado usted mismo.

Deberá crear una nueva clase que contenga tanto su propiedad como una propiedad que devuelva la colección.

0

También puede tratar de implelemnt su propia serialización utilizando la interfaz IXmlSerializable

public class ItemCollection : Collection<Item>,IXmlSerializable 
    { 
     private string _name; 

     public ItemCollection() 
     { 
      _name = string.Empty; 
     } 

     public string Name 
     { 
      get { return _name; } 
      set { _name = value; } 
     } 

#region IXmlSerializable Members 

     public System.Xml.Schema.XmlSchema GetSchema() 
     { 
       return null; 
     } 

     public void ReadXml(System.Xml.XmlReader reader) 
     { 

     } 

     public void WriteXml(System.Xml.XmlWriter writer) 
     { 
       writer.WriteElementString("name", _name); 
       List<Item> coll = new List<Item>(this.Items); 
       XmlSerializer serializer = new XmlSerializer(coll.GetType()); 
       serializer.Serialize(writer, coll); 

     } 

#endregion 
    } 

Por encima de código generará el código XML serializado como

<?xml version="1.0"?> 
<ItemCollection> 
    <name /> 
    <ArrayOfItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Item> 
     <Value>1</Value> 
    </Item> 
    <Item> 
     <Value>2</Value> 
    </Item> 
    </ArrayOfItem> 
</ItemCollection> 
+0

Parece ser una buena solución. No especificó el código para el método ReadXml y me pregunto si esto funcionaría como está. ¿Supongo que no? –

+0

Si desea deserializar de xml en algún momento, entonces tiene que escribir el ReadXML de otra manera, puede ignorar eso. Este código funciona para la serialización. –

2

No estoy seguro de si me falta algo ng, pero no desea que el XML resultante sea

<ItemCollection> 
    <Name>name val</Name> 
    <Item> 
     <Value>1</alue> 
    </Item 
    <Item> 
     <Value>2</alue> 
    </Item 
</ItemCollection> 

Si es así, solo se aplica el atributo XmlRoot a la clase ItemCollection y establecer el nombre del elemento ...

[XmlRoot(ElementName="ItemCollection")] 
public class ItemCollection : Collection<Item> 
{ 
    [XmlElement(ElementName="Name")] 
    public string Name {get;set;} 
} 

Esto le dará instrucciones al serializador para mostrar el nombre requerido para su contenedor de recolección.

0
public class Animals : List<Animal>, IXmlSerializable 
{ 
    private static Type[] _animalTypes;//for IXmlSerializable 
    public Animals() 
    { 
     _animalTypes = GetAnimalTypes().ToArray();//for IXmlSerializable 
    } 

    // this static make you access to the same Animals instance in any other class. 
    private static Animals _animals = new Animals(); 
    public static Animals animals 
    { 
     get {return _animals; } 
     set { _animals = value; } 
    } 

    #region IXmlSerializable Members 

    public System.Xml.Schema.XmlSchema GetSchema() 
    { 
     return null; 
    } 

    public void ReadXml(System.Xml.XmlReader reader) 
    { 
     bool wasEmpty = reader.IsEmptyElement; 
     reader.Read(); 
     if (wasEmpty) 
      return; 

     reader.MoveToContent(); 
     reader.ReadStartElement("Animals"); 
     // you MUST deserialize with 'List<Animal>', if Animals class has no 'List<Animal>' fields but has been derived from 'List<Animal>'. 
     List<Animal> coll = GenericSerializer.Deserialize<List<Animal>>(reader, _animalTypes); 
     // And then, You can set 'Animals' to 'List<Animal>'. 
     _animals.AddRange(coll); 
     reader.ReadEndElement(); 

     //Read Closing Element 
     reader.ReadEndElement(); 
    } 

    public void WriteXml(System.Xml.XmlWriter writer) 
    { 
     writer.WriteStartElement("Animals"); 
     // You change 'List<Animal>' to 'Animals' at first. 
     List<Animal> coll = new List<Animal>(_animals); 
     // And then, You can serialize 'Animals' with 'List<Animal>'. 
     GenericSerializer.Serialize<List<Animal>>(coll, writer, _animalTypes); 
     writer.WriteEndElement(); 
    } 

    #endregion 

    public static List<Type> GetAnimalTypes() 
    { 
     List<Type> types = new List<Type>(); 
     Assembly asm = typeof(Animals).Assembly; 
     Type tAnimal = typeof(Animal); 

     //Query our types. We could also load any other assemblies and 
     //query them for any types that inherit from Animal 
     foreach (Type currType in asm.GetTypes()) 
     { 
      if (!currType.IsAbstract 
       && !currType.IsInterface 
       && tAnimal.IsAssignableFrom(currType)) 
       types.Add(currType); 
     } 

     return types; 
    } 
}