c#继承泛型集合和序列化

时间:2009-03-20 13:07:00

标签: c# inheritance xml-serialization collections generics

设置:

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

现在,尝试使用以下代码片段进行序列化:

ItemCollection items = new ItemCollection();

...

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

查看生成的XML后,我看到ItemCollection.Name值不存在!

我认为可能发生的事情是序列化程序将ItemCollection类型视为一个简单的集合,因此忽略了任何其他添加的属性......

是否有人遇到过这样的问题并找到了解决方案?

此致

Stécy

5 个答案:

答案 0 :(得分:12)

此行为是“按设计”。从集合类派生时,Xml Seralizier将仅序列化集合元素。要解决这个问题,您应该创建一个封装集合和名称的类,并将其序列化。

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

此处提供详细讨论:http://blogs.vertigo.com/personal/chris/Blog/archive/2008/02/01/xml-serializing-a-derived-collection.aspx

答案 1 :(得分:4)

XmlSerializer是邪恶的。也就是说,任何实现IEnumerable的对象都会被序列化为一个简单的集合,忽略你自己添加的任何额外属性。

您需要创建一个包含属性和返回集合的属性的新类。

答案 2 :(得分:2)

我不确定我是否遗漏了某些内容,但您希望生成的xml是

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

如果是这样,只需将XmlRoot属性应用于itemcollection类并设置元素名称......

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

这将指示序列化程序为您的集合容器输出所需的名称。

答案 3 :(得分:0)

您还可以尝试使用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
   }

上面的代码将生成序列化的xml为

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

答案 4 :(得分: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;
    }
}