【问题标题】:c# inheriting generic collection and serializationc#继承泛型集合和序列化
【发布时间】:2010-10-14 13:01:23
【问题描述】:

设置:

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 类型视为一个简单的 Collection,因此忽略了任何其他添加的属性...

有没有人遇到过这样的问题并找到了解决办法?

问候,

斯泰西

【问题讨论】:

    标签: c# inheritance xml-serialization collections generics


    【解决方案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;
        }
    }
    

    【讨论】:

      【解决方案2】:

      此行为是“设计使然”。从集合类派生时,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;另请注意,大多数数据绑定框架也共享行为。集合具有属性根本不是一个好主意。集合有项目(仅) - 这是他们的工作。
      • 很好,现在我需要包装几个集合派生类......我担心它会使类图复杂化......
      【解决方案3】:

      我不确定我是否遗漏了什么,但您是否希望生成的 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;}
      }
      

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • 这似乎是一个很好的解决方案。您没有为 ReadXml 方法指定代码,我想知道这是否可以按原样工作。我猜它不会?
        • 如果您想在某个时候从 xml 反序列化,那么您必须编写 ReadXML 否则您可以忽略它。此代码适用于序列化。
        【解决方案5】:

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

        您将需要创建一个新类来保存您的属性和返回集合的属性。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-01
          • 1970-01-01
          相关资源
          最近更新 更多