2009-03-20 29 views
6

generic Các thiết lập:C# kế thừa bộ sưu tập và serialization

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

Bây giờ, cố gắng để serialize bằng cách sử dụng đoạn mã sau:

ItemCollection items = new ItemCollection(); 

... 

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

Khi nhìn vào XML kết quả tôi thấy rằng ItemCollection.Name giá trị không có!

Tôi nghĩ rằng những gì có thể xảy ra là các serializer thấy loại ItemCollection như một Bộ sưu tập đơn giản như vậy, bỏ qua bất kỳ thuộc tính gia tăng khác ...

Có ai đã gặp phải một vấn đề như vậy và tìm thấy một giải pháp?

Kính trọng,

Stécy

Trả lời

12

Hành vi này là "Theo thiết kế". Khi bắt nguồn từ một lớp sưu tập, Xml Seralizier sẽ chỉ tuần tự hóa các phần tử thu thập. Để giải quyết vấn đề này, bạn nên tạo một lớp đóng gói bộ sưu tập và tên và được sắp xếp theo thứ tự.

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

Một cuộc thảo luận chi tiết có sẵn ở đây: http://blogs.vertigo.com/personal/chris/Blog/archive/2008/02/01/xml-serializing-a-derived-collection.aspx

+0

+1; cũng lưu ý rằng hành vi được chia sẻ bởi hầu hết các khuôn khổ ràng buộc dữ liệu. Nó chỉ đơn giản là không phải là một ý tưởng tốt cho các bộ sưu tập để có tài sản; bộ sưu tập có các mục (chỉ) - đó là công việc của họ. –

+0

Tốt, bây giờ tôi cần bọc một số lớp học thu thập từ đó ... Tôi lo ngại rằng nó sẽ làm phức tạp sơ đồ lớp mặc dù ... –

4

XmlSerializer là ác. Điều đó nói rằng, bất kỳ đối tượng nào triển khai IEnumerable sẽ được tuần tự hóa thành một bộ sưu tập đơn giản, bỏ qua bất kỳ thuộc tính bổ sung nào mà bạn đã thêm vào.

Bạn sẽ cần phải tạo một lớp mới chứa cả thuộc tính của bạn và thuộc tính trả về bộ sưu tập.

0

Bạn cũng có thể thử implelemnt serialization của riêng bạn sử dụng giao diện 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 
    } 

Trên mã sẽ tạo ra xml serialized như

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

Điều đó có vẻ là một giải pháp tốt. Bạn đã không chỉ định mã cho phương thức ReadXml và tôi tự hỏi liệu điều này có hoạt động không. Tôi đoán là nó sẽ không? –

+0

Nếu bạn muốn deserialize từ xml tại một thời gian sau đó bạn phải viết ReadXML khôn ngoan khác bạn có thể bỏ qua điều đó. Mã này hoạt động cho tuần tự hóa. –

2

Tôi không chắc chắn nếu tôi bỏ lỡ điều gì đó ng, nhưng bạn có muốn xml kết quả là

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

Nếu vậy, chỉ cần áp dụng các thuộc tính XmlRoot đến lớp itemcollection và thiết lập tên phần tử ...

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

này sẽ hướng dẫn các serializer để xuất tên yêu cầu cho bộ sưu tập của bạ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; 
    } 
}