一种通用列表反序列化类?

时间:2009-09-25 14:12:52

标签: c# serialization xml-serialization

好的,到目前为止,这是故事。

我已经可以使用XmlSerializer对单个对象进行反序列化,但反序列化列表确实令人头疼。我开始尝试序列化List<Foo>,并且序列化程序在根<Foo>元素内序列化了多个<ArrayOfFoo> XML结构。这被证明是反序列化的问题,所以看起来我需要自己定义'ArrayOfFoo'元素。所以,我有一个类工作,这是列表的'包装',如该程序所示:

using System;
using System.IO;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace XmlTester2
{
    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("XML tester...");

            string xml =
                "<ItemList xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                "<Person i:type=\"PersonI2\">" + "<Field1>field1Val</Field1>" +
                "<Field2>field2Val</Field2>" + "<Field3>field3Val</Field3>" +
                "<Field4>field4Val</Field4>" + "</Person>" +
                "<Account i:type=\"AccountI2\">" + "<Field1>field1Val</Field1>" +
                "<Field2>field2Val</Field2>" + "<Field3>field3Val</Field3>" +
                "<Field4>field4Val</Field4>" + "</Account>" +
                "<Person i:type=\"PersonI2\">" + "<Field1>field1Val</Field1>" +
                "<Field2>field2Val</Field2>" + "<Field3>field3Val</Field3>" +
                "<Field4>field4Val</Field4>" + "</Person>" + "</ItemList>";

            XmlSerializer ser = new XmlSerializer(typeof(ItemList));

            using (var reader = new StringReader(xml))
            {
                ItemList result = (ItemList)ser.Deserialize(reader);
            }

            Console.WriteLine("Break here and check 'result' in Quickwatch...");
            Console.ReadKey();
        }
    }

    [XmlRootAttribute(IsNullable = false)]
    public class ItemList
    {
        [XmlElementAttribute("Person")]
        public List<Person> Persons { get; set; }

        [XmlElementAttribute("Account")]
        public List<Account> Accounts { get; set; }
    }

    [XmlTypeAttribute(AnonymousType = false, TypeName = "Person", Namespace = "")]
    [XmlInclude(typeof(PersonI2))]
    public class Person
    {
        public string Field1 { get; set; }
        public string Field2 { get; set; }
        public string Field3 { get; set; }
    }

    [XmlTypeAttribute(AnonymousType = false, TypeName = "PersonI2", Namespace = "")]
    public class PersonI2 : Person
    {
        public string Field4 { get; set; }
    }

    [XmlTypeAttribute(AnonymousType = false, TypeName = "Account", Namespace = "")]
    [XmlInclude(typeof(AccountI2))]
    public class Account
    {
        public string Field1 { get; set; }
        public string Field2 { get; set; }
        public string Field3 { get; set; }
    }

    [XmlTypeAttribute(AnonymousType = false, TypeName = "AccountI2", Namespace = "")]
    public class AccountI2 : Account
    {
        public string Field4 { get; set; }
    }
}

但是,这个'包装器'ItemList仍然必须在其中手动定义可能包含的所有元素(在示例中为Person和Account)。真正理想的是拥有一个通用的列表包装类。我知道这有点充满希望,但有没有办法做到这一点?我正在考虑这些问题(这不起作用,但只是为了给你一般的想法):

using System;
using System.IO;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace XmlTester3
{
    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("XML tester...");

            string xml =
                "<ItemList xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                "<Person i:type=\"PersonI2\">" + 
                "<Field1>field1Val</Field1>" +
                "<Field2>field2Val</Field2>" + 
                "<Field3>field3Val</Field3>" +
                "<Field4>field4Val</Field4>" + 
                "</Person>" +
                "<Person i:type=\"PersonI2\">" + 
                "<Field1>field1Val</Field1>" +
                "<Field2>field2Val</Field2>" + 
                "<Field3>field3Val</Field3>" +
                "<Field4>field4Val</Field4>" + 
                "</Person>" + 
                "</ItemList>";

            XmlSerializer ser = new XmlSerializer(typeof(ItemList<Person>));

            using (var reader = new StringReader(xml))
            {
                ItemList<Person> result = (ItemList<Person>)ser.Deserialize(reader);
            }

            Console.WriteLine("Break here and check 'result' in Quickwatch...");
            Console.ReadKey();
        }
    }

    [XmlRootAttribute(IsNullable = false)]
    [XmlInclude(typeof(Person))]
    [XmlInclude(typeof(PersonI2))]
    [XmlInclude(typeof(Account))]
    [XmlInclude(typeof(AccountI2))]
    public class ItemList<T>
    {
        [XmlElementAttribute]
        public List<T> Items { get; set; }
    }

    [XmlTypeAttribute(AnonymousType = false, TypeName = "Person", Namespace = "")]
    [XmlInclude(typeof(PersonI2))]
    public class Person
    {
        public string Field1 { get; set; }
        public string Field2 { get; set; }
        public string Field3 { get; set; }
    }

    [XmlTypeAttribute(AnonymousType = false, TypeName = "PersonI2", Namespace = "")]
    public class PersonI2 : Person
    {
        public string Field4 { get; set; }
    }

    [XmlTypeAttribute(AnonymousType = false, TypeName = "Account", Namespace = "")]
    [XmlInclude(typeof(AccountI2))]
    public class Account
    {
        public string Field1 { get; set; }
        public string Field2 { get; set; }
        public string Field3 { get; set; }
    }

    [XmlTypeAttribute(AnonymousType = false, TypeName = "AccountI2", Namespace = "")]
    public class AccountI2 : Account
    {
        public string Field4 { get; set; }
    }
}

因此,ItemList内部传递的XML结构只能是一种类型,比如本例中的Person,我可以定义一个允许我的ItemList<Person>反序列化包含多个Person对象的列表?有任何想法吗?如有必要,我不介意为ItemList可能包含其集合的每种类型标记[XmlInclude...]ItemList

我猜这是可能的,我还没弄明白怎么样? :-)或者是默认的XmlSerializer太挑剔了吗?

6 个答案:

答案 0 :(得分:3)

您可以轻松地完成此操作,只需实现System.Xml.Serialization.IXmlSerializable界面即可。如果我这样做,我甚至可能会在定义T的程序集中反映可能的T派生类型,并完全省略[XmlInclude]声明。这种方法的真正缺点是创建了XmlSerializers。您可以考虑缓存它们。无论如何只是在你的第二个例子中使用它,它应该工作。

顺便说一下,你用“i:type = \”PersonI2 \“”做一件有趣的事情;把这个拿出去的道具;)

[XmlRootAttribute("ItemList", IsNullable = false)]
[XmlInclude(typeof(Person))]
[XmlInclude(typeof(PersonI2))]
[XmlInclude(typeof(Account))]
[XmlInclude(typeof(AccountI2))]
public class ItemList<T> : System.Xml.Serialization.IXmlSerializable
{
    class Map : Dictionary<String, XmlSerializer> 
    { public Map() : base(StringComparer.Ordinal) { } }

    public List<T> Items { get; set; }

    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    private string TypeName(Type t)
    {
        String typeName = t.Name;
        foreach (XmlTypeAttribute a in t.GetCustomAttributes(typeof(XmlTypeAttribute), true))
            if (!String.IsNullOrEmpty(a.TypeName))
                typeName = a.TypeName;
        return typeName;
    }

    private Map LoadSchema()
    {
        Map map = new Map();
        foreach (XmlIncludeAttribute inc in typeof(ItemList<T>).GetCustomAttributes(typeof(XmlIncludeAttribute), true))
        {
            Type t = inc.Type;
            if (typeof(T).IsAssignableFrom(t))
                map.Add(TypeName(t), new XmlSerializer(t));
        }
        return map;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        Map map = LoadSchema();
        int depth = reader.Depth;

        List<T> items = new List<T>();
        if (!reader.IsEmptyElement && reader.Read())
        {
            while (reader.Depth > depth)
            {
                items.Add((T)map[reader.LocalName].Deserialize(reader));
            }
        }
        this.Items = items;
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        Map map = LoadSchema();
        foreach (T item in this.Items)
        {
            map[TypeName(item.GetType())].Serialize(writer, item);
        }
    }
}

答案 1 :(得分:2)

我不确定我理解你的问题,但你知道有XmlArrayItemAttribute

[XmlArray("foos"), XmlArrayItem(typeof(Foo), ElementName = "foo")]

答案 2 :(得分:1)

在.NET 3.5 SP1(特定是SP1)下,您可以使用WCF中的Serializers来反序列化对象,而无需使用DataContract或Serializable属性特定地标记该类。

几乎任何类都应该能够以这种方式反序列化 - 只要属性名称与元素名称匹配即可。

如果您遇到反序列化错误 - 那么可能是因为某些名称不正确的属性或类型不正确。要检查Serializer要查找的输入,可以填充一次对象,然后将其序列化为XML进行比较。

我自己写了一个辅助课程,以便暂时使用它。

使用帮助程序的方法是:

string serialized = "some xml";
MyType foo = Helpers.Deserialize<MyType>(serialized, SerializerType.Xml); 

实际的助手类:

using System.IO;
using System.Runtime.Serialization; // System.Runtime.Serialization.dll (.NET 3.0)
using System.Runtime.Serialization.Json; // System.ServiceModel.Web.dll (.NET 3.5)
using System.Text;
namespace Serialization
{
    public static class Helpers
    {
        /// <summary>
        /// Declare the Serializer Type you want to use.
        /// </summary>
        public enum SerializerType
        {
            Xml, // Use DataContractSerializer
            Json // Use DataContractJsonSerializer
        }

        public static T Deserialize<T>(string SerializedString, SerializerType UseSerializer)
        {
            // Get a Stream representation of the string.
            using (Stream s = new MemoryStream(UTF8Encoding.UTF8.GetBytes(SerializedString)))
            {
                T item;
                switch (UseSerializer)
                {
                    case SerializerType.Json:
                        // Declare Serializer with the Type we're dealing with.
                        var serJson = new DataContractJsonSerializer(typeof(T));
                        // Read(Deserialize) with Serializer and cast
                        item = (T)serJson.ReadObject(s);
                        break;
                    case SerializerType.Xml:
                    default:
                        var serXml = new DataContractSerializer(typeof(T));
                        item = (T)serXml.ReadObject(s);
                        break;
                }
                return item;
            }
        }

        public static string Serialize<T>(T ObjectToSerialize, SerializerType UseSerializer)
        {
            using (MemoryStream serialiserStream = new MemoryStream())
            {
                string serialisedString = null;
                switch (UseSerializer)
                {
                    case SerializerType.Json:
                        // init the Serializer with the Type to Serialize
                        DataContractJsonSerializer serJson = new DataContractJsonSerializer(typeof(T));
                        // The serializer fills the Stream with the Object's Serialized Representation.
                        serJson.WriteObject(serialiserStream, ObjectToSerialize);
                        break;
                    case SerializerType.Xml:
                    default:
                        DataContractSerializer serXml = new DataContractSerializer(typeof(T));
                        serXml.WriteObject(serialiserStream, ObjectToSerialize);
                        break;
                }
                // Rewind the stream to the start so we can now read it.
                serialiserStream.Position = 0;
                using (StreamReader sr = new StreamReader(serialiserStream))
                {
                    // Use the StreamReader to get the serialized text out
                    serialisedString = sr.ReadToEnd();
                    sr.Close();
                }
                return serialisedString;
            }
        }
    }
}

答案 3 :(得分:0)

(de)序列化对象有两种主要技术:

  1. 为每个要(de)序列化的类实现一个接口及其Serialize()和Deserialize()方法 - 快速但需要大量维护。

  2. 使用基于反射的serizlier / deserializer来分析类中的公共字段和属性 - 较慢但不需要在每个类中维护(de)serialize()方法。

  3. 就个人而言,在很多情况下,我更喜欢第二种技术。

    .NET内置的XmlSerializer支持第二种技术,但有许多限制:

    1。多级数组。

    2。反序列化意外类型的对象:

        public MyClass
        {
            public IMyInterface MyProperty1
            {
                get;
                set;
            }
    
            public MyBaseType MyProperty2
            {
                get;
                set;
            }
        }
    

    反序列化期间MyProperty1,MyProperty2中的实际对象类型未知。
    3。 (De)序列化复杂的集合
    4。没有好办法处理在序列化和反序列化之间添加/删除字段/属性的情况。

    5。不支持使用循环序列化图形。

    <小时/>

    我想出的解决方案是编写基于自定义反射的序列化器/解串器, 当时我找不到任何现有的序列化器,所以我从头开始写了一个新的序列化器。 我不能发布它,因为它是专有的,但是我注意到之后发布了类似的序列化器:

    http://www.codeproject.com/KB/XML/GR_CustomXmlSerializer.aspx
    XML Serialization and Inherited Types
    http://www.codeproject.com/KB/XML/deepserializer.aspx

答案 4 :(得分:0)

更新:请从答案开始!这是我发现的最佳解决方案! - 这是比这个更好的解决方案。

...

深受csharptest.net评论的启发,我创建了一个几乎完成我想要的工作的课程。 :-)您可以通过检查ItemList.Items来访问反序列化的项目,并通过将项目插入ItemList.Items来序列化东西,然后使用适当的XmlSerializer对其进行序列化。唯一的轻微烦恼是你必须确保ItemList类被标记为每个可能需要(反)序列化的类类型的XmlIncludeAttribute,否则XmlSerializer将无法处理它。

这是示例程序,包含通用ItemList类:

using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace XmlTester
{
 public class Program {
  static void Main(string[] args) {
   Console.WriteLine("XML tester...");

// Valid XML for an ItemList of Person's
XmlSerializer ser = new XmlSerializer(typeof(ItemList<Person>));
string xmlIn =
@"<ItemList xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
 <PersonBilingual>
  <FullName>John Smith</FullName>
  <Age>21</Age>
  <Language>French</Language>
  <SecondLanguage>German</SecondLanguage>
 </PersonBilingual>
 <Person>
  <FullName>Joe Bloggs</FullName>
  <Age>26</Age>
  <Language>English</Language>
 </Person>
 <Person i:type=""PersonBilingual"">
  <FullName>Jane Doe</FullName>
  <Age>78</Age>
  <Language>Italian</Language>
  <SecondLanguage>English</SecondLanguage>
 </Person>
</ItemList>";

//// Valid XML for an ItemList of Account's
//XmlSerializer ser = new XmlSerializer(typeof(ItemList<Account>));
//string xmlIn =
//@"<ItemList xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
// <AccountBank>
//  <AcctName>Deposit account</AcctName>
//  <WithCompany>Bank of Switzerland</WithCompany>
//  <BalanceInEuros>300</BalanceInEuros>
// </AccountBank>
// <Account>
//  <AcctName>Book buying account</AcctName>
//  <WithCompany>Amazon</WithCompany>
// </Account>
// <Account i:type=""AccountBank"">
//  <AcctName>Savings account</AcctName>
//  <WithCompany>Bank of America</WithCompany>
//  <BalanceInEuros>2500</BalanceInEuros>
// </Account>
//</ItemList>";

//// Invalid XML, as we have mixed incompatible types
//XmlSerializer ser = new XmlSerializer(typeof(ItemList<Person>));
//string xmlIn =
//@"<ItemList xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
// <PersonBilingual>
//  <FullName>John Smith</FullName>
//  <Age>21</Age>
//  <Language>French</Language>
//  <SecondLanguage>German</SecondLanguage>
// </PersonBilingual>
// <Account>
//  <AcctName>Book buying account</AcctName>
//  <WithCompany>Amazon</WithCompany>
// </Account>
// <Person i:type=""PersonBilingual"">
//  <FullName>Jane Doe</FullName>
//  <Age>78</Age>
//  <Language>Italian</Language>
//  <SecondLanguage>English</SecondLanguage>
// </Person>
//</ItemList>";

   // Deserialize...
   ItemList<Person> result;
   using (var reader = new StringReader(xmlIn)) {
    result = (ItemList<Person>)ser.Deserialize(reader);
   }

   Console.WriteLine("Break here and check 'result' in Quickwatch...");
   Console.ReadKey();

   // Serialize...
   StringBuilder xmlOut = new StringBuilder();
   ser.Serialize(new StringWriter(xmlOut), result);

   Console.WriteLine("Break here and check 'xmlOut' in Quickwatch...");
   Console.ReadKey();
  }
 }

 [XmlRoot(ElementName = "ItemList", IsNullable = false)]
 [XmlInclude(typeof(Person))]
 [XmlInclude(typeof(PersonBilingual))]
 [XmlInclude(typeof(Account))]
 [XmlInclude(typeof(AccountBank))]
 public class ItemList<T> : IXmlSerializable {
  #region Private vars

  /// <summary>
  /// The class that will store our serializers for the various classes that may be (de)serialized, given
  /// the type of this ItemList (ie. the type itself, as well as any type that extends the type)
  /// </summary>
  private class Map : Dictionary<string, XmlSerializer> { public Map() : base(StringComparer.Ordinal) { } }

  #endregion

  #region Private methods

  /// <summary>
  /// Creates a 'schema' for this ItemList, using its type, and the XmlIncludeAttribute types that are
  /// associated with it.  For each XmlIncludeAttribute, if it can be assigned to this ItemList's type (so
  /// it's either the same type as this ItemList's type or a type that extends this ItemList's type), adds
  /// the XmlSerializer for that XmlIncludeAttribute's type to our 'schema' collection, allowing a node
  /// corresponding to that type to be (de)serialized by this ItemList.
  /// </summary>
  /// <returns>The 'schema' containing the XmlSerializer's available for this ItemList to use during (de)serialization.</returns>
  private Map loadSchema() {
   Map map = new Map();
   foreach (XmlIncludeAttribute inc in typeof(ItemList<T>).GetCustomAttributes(typeof(XmlIncludeAttribute), true)) {
    Type t = inc.Type;
    if (typeof(T).IsAssignableFrom(t)) { map.Add(xmlTypeName(t), new XmlSerializer(t)); }
   }
   return map;
  }

  /// <summary>
  /// As the XML type name can be different to our internal class name for that XML type, we need to be able
  /// to expect an XML element name that is different to our internal class name for that XML type.  Hence,
  /// our 'schema' map will contain XmlSerializer's whose keys are based on the XML type name, NOT our
  /// internal class name for that XML type.  This method returns the XML type name given our internal
  /// class we're using to (de)serialize that XML type.  If no XML TypeName is specified in our internal
  /// class's XmlTypeAttribute, we assume an XML type name identical to the internal class name.
  /// </summary>
  /// <param name="t">Our internal class used to (de)serialize an XML type.</param>
  /// <returns>The XML type name corresponding to the given internal class.</returns>
  private string xmlTypeName(Type t) {
   string typeName = t.Name;
   foreach (XmlTypeAttribute ta in t.GetCustomAttributes(typeof(XmlTypeAttribute), true)) {
    if (!string.IsNullOrEmpty(ta.TypeName)) { typeName = ta.TypeName; }
   }
   return typeName;
  }

  #endregion

  #region IXmlSerializable Members

  /// <summary>
  /// Reserved and should not be used.
  /// </summary>
  /// <returns>Must return null.</returns>
  public XmlSchema GetSchema() {
   return null;
  }

  /// <summary>
  /// Generates a list of type T objects from their XML representation; stores them in this.Items.
  /// </summary>
  /// <param name="reader">The System.Xml.XmlReader stream from which the objects are deserialized.</param>
  public void ReadXml(XmlReader reader) {
   Map map = loadSchema();
   int depth = reader.Depth;

   List<T> items = new List<T>();
   if (!reader.IsEmptyElement && reader.Read()) {
    // While the reader is at a greater depth that the initial depth, ie. at one of the elements
    // inside the list wrapper, the initial depth being that of the list wrapper <ItemList>...
    while (reader.Depth > depth) {
     try { items.Add((T)map[reader.LocalName].Deserialize(reader)); }
     catch (InvalidOperationException iopEx) {
      if (
       iopEx.InnerException != null &&
       iopEx.InnerException.Message.StartsWith("The specified type was not recognized")
      ) { throw new InvalidOperationException("Couldn't deserialize node '" + reader.LocalName + "' because although its element node is a valid type, its attribute-specified type was not recognized.  Perhaps it needs adding to the ItemList using XmlIncludeAttribute?", iopEx); }
     }
     catch (KeyNotFoundException knfEx) {
      throw new InvalidOperationException("Couldn't deserialize node '" + reader.LocalName + "' because its element node was not recognized as a valid type.  Perhaps it needs adding to the ItemList using XmlIncludeAttribute?", knfEx);
     }
     catch (Exception ex) {
      throw ex;
     }
    }
   }
   this.Items = items;
  }

  /// <summary>
  /// Converts a list of type T objects into their XML representation; writes them to the specified writer.
  /// </summary>
  /// <param name="writer">The System.Xml.XmlWriter stream to which the objects are serialized.</param>
  public void WriteXml(XmlWriter writer) {
   Map map = loadSchema();
   foreach (T item in this.Items) {
    map[xmlTypeName(item.GetType())].Serialize(writer, item);
   }
  }

  #endregion

  #region Public properties

  public List<T> Items { get; set; }

  #endregion
 }

 /// <summary>
 /// A regular person.
 /// </summary>
 [XmlType(AnonymousType = false, TypeName = "Person", Namespace = "")]
 [XmlInclude(typeof(PersonBilingual))]
 public class Person {
  public string FullName { get; set; }
  public int Age { get; set; }
  public string Language { get; set; }
 }

 /// <summary>
 /// A person who can speak a second language.
 /// </summary>
 [XmlType(AnonymousType = false, TypeName = "PersonBilingual", Namespace = "")]
 public class PersonBilingual : Person {
  public string SecondLanguage { get; set; }
 }

 /// <summary>
 /// Some kind of account.
 /// </summary>
 [XmlType(AnonymousType = false, TypeName = "Account", Namespace = "")]
 [XmlInclude(typeof(AccountBank))]
 public class Account {
  public string AcctName { get; set; }
  public string WithCompany { get; set; }
 }

 /// <summary>
 /// A bank account.
 /// </summary>
 [XmlType(AnonymousType = false, TypeName = "AccountBank", Namespace = "")]
 public class AccountBank : Account {
  public int BalanceInEuros { get; set; }
 }
}

感谢大家的帮助!

答案 5 :(得分:0)

!这是我发现的最好的解决方案!

好的,对不起这里的答案垃圾邮件,人们,但是我发现了一种更优雅的方法,这样就可以避免使用ItemList属性来访问ItemList。使ItemList成为一个List本身!这样,您只需直接访问ItemList作为列表。这是修改后的示例程序:

using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace XmlTester
{
    public class Program {
        static void Main(string[] args) {
            Console.WriteLine("XML tester...");

// Valid XML for an ItemList of Person's
XmlSerializer ser = new XmlSerializer(typeof(ItemList<Person>));
string xmlIn =
@"<ItemList xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
    <PersonBilingual>
        <FullName>John Smith</FullName>
        <Age>21</Age>
        <Language>French</Language>
        <SecondLanguage>German</SecondLanguage>
    </PersonBilingual>
    <Person>
        <FullName>Joe Bloggs</FullName>
        <Age>26</Age>
        <Language>English</Language>
    </Person>
    <Person i:type=""PersonBilingual"">
        <FullName>Jane Doe</FullName>
        <Age>78</Age>
        <Language>Italian</Language>
        <SecondLanguage>English</SecondLanguage>
    </Person>
</ItemList>";

//// Valid XML for an ItemList of Account's
//XmlSerializer ser = new XmlSerializer(typeof(ItemList<Account>));
//string xmlIn =
//@"<ItemList xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
//  <AccountBank>
//      <AcctName>Deposit account</AcctName>
//      <WithCompany>Bank of Switzerland</WithCompany>
//      <BalanceInEuros>300</BalanceInEuros>
//  </AccountBank>
//  <Account>
//      <AcctName>Book buying account</AcctName>
//      <WithCompany>Amazon</WithCompany>
//  </Account>
//  <Account i:type=""AccountBank"">
//      <AcctName>Savings account</AcctName>
//      <WithCompany>Bank of America</WithCompany>
//      <BalanceInEuros>2500</BalanceInEuros>
//  </Account>
//</ItemList>";

//// Invalid XML, as we have mixed incompatible types
//XmlSerializer ser = new XmlSerializer(typeof(ItemList<Person>));
//string xmlIn =
//@"<ItemList xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
//  <PersonBilingual>
//      <FullName>John Smith</FullName>
//      <Age>21</Age>
//      <Language>French</Language>
//      <SecondLanguage>German</SecondLanguage>
//  </PersonBilingual>
//  <Account>
//      <AcctName>Book buying account</AcctName>
//      <WithCompany>Amazon</WithCompany>
//  </Account>
//  <Person i:type=""PersonBilingual"">
//      <FullName>Jane Doe</FullName>
//      <Age>78</Age>
//      <Language>Italian</Language>
//      <SecondLanguage>English</SecondLanguage>
//  </Person>
//</ItemList>";

            // Deserialize...
            ItemList<Person> result;
            using (var reader = new StringReader(xmlIn)) {
                result = (ItemList<Person>)ser.Deserialize(reader);
            }

            Console.WriteLine("Break here and check 'result' in Quickwatch...");
            Console.ReadKey();

            // Serialize...
            StringBuilder xmlOut = new StringBuilder();
            ser.Serialize(new StringWriter(xmlOut), result);

            Console.WriteLine("Break here and check 'xmlOut' in Quickwatch...");
            Console.ReadKey();
        }
    }

    [XmlRoot(ElementName = "ItemList", IsNullable = false)]
    [XmlInclude(typeof(Person))]
    [XmlInclude(typeof(PersonBilingual))]
    [XmlInclude(typeof(Account))]
    [XmlInclude(typeof(AccountBank))]
    public class ItemList<T> : List<T>, IXmlSerializable {
        #region Private vars

        /// <summary>
        /// The class that will store our serializers for the various classes that may be (de)serialized, given
        /// the type of this ItemList (ie. the type itself, as well as any type that extends the type)
        /// </summary>
        private class Map : Dictionary<string, XmlSerializer> { public Map() : base(StringComparer.Ordinal) { } }

        #endregion

        #region Private methods

        /// <summary>
        /// Creates a 'schema' for this ItemList, using its type, and the XmlIncludeAttribute types that are
        /// associated with it.  For each XmlIncludeAttribute, if it can be assigned to this ItemList's type (so
        /// it's either the same type as this ItemList's type or a type that extends this ItemList's type), adds
        /// the XmlSerializer for that XmlIncludeAttribute's type to our 'schema' collection, allowing a node
        /// corresponding to that type to be (de)serialized by this ItemList.
        /// </summary>
        /// <returns>The 'schema' containing the XmlSerializer's available for this ItemList to use during (de)serialization.</returns>
        private Map loadSchema() {
            Map map = new Map();
            foreach (XmlIncludeAttribute inc in typeof(ItemList<T>).GetCustomAttributes(typeof(XmlIncludeAttribute), true)) {
                Type t = inc.Type;
                if (typeof(T).IsAssignableFrom(t)) { map.Add(xmlTypeName(t), new XmlSerializer(t)); }
            }
            return map;
        }

        /// <summary>
        /// As the XML type name can be different to our internal class name for that XML type, we need to be able
        /// to expect an XML element name that is different to our internal class name for that XML type.  Hence,
        /// our 'schema' map will contain XmlSerializer's whose keys are based on the XML type name, NOT our
        /// internal class name for that XML type.  This method returns the XML type name given our internal
        /// class we're using to (de)serialize that XML type.  If no XML TypeName is specified in our internal
        /// class's XmlTypeAttribute, we assume an XML type name identical to the internal class name.
        /// </summary>
        /// <param name="t">Our internal class used to (de)serialize an XML type.</param>
        /// <returns>The XML type name corresponding to the given internal class.</returns>
        private string xmlTypeName(Type t) {
            string typeName = t.Name;
            foreach (XmlTypeAttribute ta in t.GetCustomAttributes(typeof(XmlTypeAttribute), true)) {
                if (!string.IsNullOrEmpty(ta.TypeName)) { typeName = ta.TypeName; }
            }
            return typeName;
        }

        #endregion

        #region IXmlSerializable Members

        /// <summary>
        /// Reserved and should not be used.
        /// </summary>
        /// <returns>Must return null.</returns>
        public XmlSchema GetSchema() {
            return null;
        }

        /// <summary>
        /// Generates a list of type T objects from their XML representation; stores them in this ItemList.
        /// </summary>
        /// <param name="reader">The System.Xml.XmlReader stream from which the objects are deserialized.</param>
        public void ReadXml(XmlReader reader) {
            Map map = loadSchema();
            int depth = reader.Depth;

            List<T> items = new List<T>();
            if (!reader.IsEmptyElement && reader.Read()) {
                // While the reader is at a greater depth that the initial depth, ie. at one of the elements
                // inside the list wrapper, the initial depth being that of the list wrapper <ItemList>...
                while (reader.Depth > depth) {
                    try { items.Add((T)map[reader.LocalName].Deserialize(reader)); }
                    catch (InvalidOperationException iopEx) {
                        if (
                            iopEx.InnerException != null &&
                            iopEx.InnerException.Message.StartsWith("The specified type was not recognized")
                        ) { throw new InvalidOperationException("Couldn't deserialize node '" + reader.LocalName + "' because although its element node is a valid type, its attribute-specified type was not recognized.  Perhaps it needs adding to the ItemList using XmlIncludeAttribute?", iopEx); }
                    }
                    catch (KeyNotFoundException knfEx) {
                        throw new InvalidOperationException("Couldn't deserialize node '" + reader.LocalName + "' because its element node was not recognized as a valid type.  Perhaps it needs adding to the ItemList using XmlIncludeAttribute?", knfEx);
                    }
                    catch (Exception ex) {
                        throw ex;
                    }
                }
            }
            this.AddRange(items);
        }

        /// <summary>
        /// Converts a list of type T objects into their XML representation; writes them to the specified writer.
        /// </summary>
        /// <param name="writer">The System.Xml.XmlWriter stream to which the objects are serialized.</param>
        public void WriteXml(XmlWriter writer) {
            Map map = loadSchema();
            foreach (T item in this) {
                map[xmlTypeName(item.GetType())].Serialize(writer, item);
            }
        }

        #endregion
    }

    /// <summary>
    /// A regular person.
    /// </summary>
    [XmlType(AnonymousType = false, TypeName = "Person", Namespace = "")]
    [XmlInclude(typeof(PersonBilingual))]
    public class Person {
        public string FullName { get; set; }
        public int Age { get; set; }
        public string Language { get; set; }
    }

    /// <summary>
    /// A person who can speak a second language.
    /// </summary>
    [XmlType(AnonymousType = false, TypeName = "PersonBilingual", Namespace = "")]
    public class PersonBilingual : Person {
        public string SecondLanguage { get; set; }
    }

    /// <summary>
    /// Some kind of account.
    /// </summary>
    [XmlType(AnonymousType = false, TypeName = "Account", Namespace = "")]
    [XmlInclude(typeof(AccountBank))]
    public class Account {
        public string AcctName { get; set; }
        public string WithCompany { get; set; }
    }

    /// <summary>
    /// A bank account.
    /// </summary>
    [XmlType(AnonymousType = false, TypeName = "AccountBank", Namespace = "")]
    public class AccountBank : Account {
        public int BalanceInEuros { get; set; }
    }
}