2009-03-20 10 views
6

Konfiguracja:C# dziedziczeniu rodzajowej kolekcji i serializacji

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

Teraz, starając się szeregować stosując następujący fragment kodu:

ItemCollection items = new ItemCollection(); 

... 

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

Po patrząc na wynikowym pliku XML widzę, że ItemCollection.Name wartość nie ma!

I pomyśleć, co może się dziać to, że serializer widzi typ ItemCollection jak zwykłą Collection w ten sposób ignorowanie wszelkich innych dodanych właściwości ...

Czy ktoś napotkały na taki problem i znalazł rozwiązanie?

Pozdrawiam,

Stecy

Odpowiedz

12

To zachowanie jest "według projektu". Gdy wywodzi się z klasy kolekcji, Xml Seralizier będzie tylko serializować elementy kolekcji. Aby obejść ten problem, powinieneś utworzyć klasę, która zawiera kolekcję i nazwę, i która została zserializowana.

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

Szczegółowe omówienie jest dostępny tutaj: http://blogs.vertigo.com/personal/chris/Blog/archive/2008/02/01/xml-serializing-a-derived-collection.aspx

+0

+1; zauważ również, że zachowanie jest również wspólne dla większości frameworków wiążących dane. Po prostu nie jest dobrym pomysłem, by kolekcje miały własności; kolekcje mają przedmioty (tylko) - to jest ich praca. –

+0

Fajnie, teraz muszę zawinąć kilka klas pochodzących z kolekcji, a następnie ... Obawiam się, że utrudniłoby to diagram klasowy ... –

4

XmlSerializer jest zła. Powiedział, że każdy obiekt, który implementuje IEnumerable, będzie serializowany jako prosta kolekcja, ignorując wszelkie dodatkowe właściwości, które sam dodałeś.

Należy utworzyć nową klasę, która będzie zawierała zarówno właściwość, jak i właściwość zwracającą kolekcję.

0

Można także spróbować implelemnt własną serializacji użyciu IXmlSerializable interfejs

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 
    } 

Powyższy kod wygeneruje zserializowaną XML jako

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

To wydaje się być dobrym rozwiązaniem. Nie określiłeś kodu dla metody ReadXml i zastanawiam się, czy to działa tak jak jest. Zgaduję, że nie? –

+0

Jeśli chcesz deserializować z xml w pewnym momencie, musisz napisać ReadXML w inny sposób, możesz to zignorować. Ten kod działa w celu serializacji. –

2

Nie jestem pewien, czy brakuje mi czegoś ng, ale chcesz wynikowy xml być

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

Jeżeli tak, wystarczy zastosować atrybut XmlRoot do klasy itemcollection i ustawić nazwę elementu ...

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

To poinstruuje serializatora aby wypisać wymaganą nazwę dla twojego pojemnika do zbierania.

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; 
    } 
} 
Powiązane problemy