6 Stimmen

c# vererben generische Sammlung und Serialisierung

Der Aufbau:

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

Nun versuche ich, mit dem folgenden Codefragment zu serialisieren:

ItemCollection items = new ItemCollection();

...

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

Wenn ich mir die resultierende XML-Datei ansehe, sehe ich, dass der Wert ItemCollection.Name nicht vorhanden ist!

Ich denke, was passieren kann, ist, dass der Serialisierer den ItemCollection-Typ als eine einfache Sammlung sieht und daher alle anderen hinzugefügten Eigenschaften ignoriert...

Ist jemand auf ein solches Problem gestoßen und hat eine Lösung gefunden?

Herzliche Grüße,

Stécy

12voto

JaredPar Punkte 699699

Dieses Verhalten ist "By Design". Bei der Ableitung von einer Sammlungsklasse wird der Xml-Seralisierer nur die Sammlungselemente serialisieren. Um dies zu umgehen, sollten Sie eine Klasse erstellen, die die Sammlung und den Namen kapselt, und diese serialisieren lassen.

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

Eine ausführliche Diskussion finden Sie hier: http://blogs.vertigo.com/personal/chris/Blog/archive/2008/02/01/xml-serializing-a-derived-collection.aspx

4voto

sisve Punkte 19163

XmlSerializer ist böse. Das heißt, jedes Objekt, das IEnumerable implementiert, wird als einfache Sammlung serialisiert und ignoriert alle zusätzlichen Eigenschaften, die Sie selbst hinzugefügt haben.

Sie müssen eine neue Klasse erstellen, die sowohl Ihre Eigenschaft als auch eine Eigenschaft enthält, die die Sammlung zurückgibt.

2voto

NoelAdy Punkte 224

Ich bin mir nicht sicher, ob ich etwas übersehe, aber wollen Sie, dass die resultierende xml sein

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

Wenn ja, wenden Sie einfach das XmlRoot-Attribut auf die itemcollection-Klasse an und setzen den Elementnamen...

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

Dadurch wird der Serialisierer angewiesen, den erforderlichen Namen für Ihren Sammelcontainer auszugeben.

0voto

gk. Punkte 1222

Sie können auch versuchen, Ihre eigene Serialisierung mithilfe der Schnittstelle IXmlSerializable zu implementieren

    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
   }

Der obige Code erzeugt die serialisierte Xml-Datei als

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

0voto

Brad Jeong Punkte 1
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;
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X