代码之家  ›  专栏  ›  技术社区  ›  Jez

一种泛型列表反序列化类?

  •  1
  • Jez  · 技术社区  · 16 年前

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

    XmlSerializer ,但反序列化列表被证明是一个真正令人头痛的问题。我一开始试着序列化 List<Foo> 序列化程序序列化了多个 <Foo> 根目录中的XML结构 <ArrayOfFoo> 要素事实证明,反序列化是有问题的,所以看起来我需要自己定义“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

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

    因此,XML结构在 线性对象表 只能是一种类型,比如说 Person 在这个例子中,我可以定义 ItemList<Person> 这将允许我反序列化包含多个Person对象的列表?有什么想法吗?如果有必要的话,我不介意给这个房间贴标签 线性对象表 [XmlInclude...] 每种类型 可能包含以下内容的集合。

    我猜这是可能的,我只是还不知道怎么做?:-)还是默认的XmlSerializer过于繁琐?

    6 回复  |  直到 16 年前
        1
  •  3
  •   csharptest.net    16 年前

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

    顺便说一句,您使用“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);
            }
        }
    }
    
        2
  •  2
  •   bruno conde    16 年前

    我不确定我是否理解你的问题,但你知道有一个问题吗 XmlArrayItemAttribute .

    [XmlArray("foos"), XmlArrayItem(typeof(Foo), ElementName = "foo")]
    
        3
  •  1
  •   user111013 user111013    16 年前

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

    如果您遇到反序列化程序错误,则可能是因为某些属性命名错误或类型不正确。要检查序列化程序正在查找的输入,可以填充一次对象,然后将其序列化为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;
                }
            }
        }
    }
    
        4
  •  0
  •   Community Mohan Dere    8 年前

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

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

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



    1.多维数组。


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

    在反序列化过程中,MyProperty1、MyProperty2中实际对象的类型未知。
    3.(反)序列化复杂集合。



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


    我提出的解决方案是编写一个自定义的基于反射的序列化器/反序列化器, 我无法发布它,因为它是专有的,但是我注意到,后来发布了simular序列化程序:

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

        5
  •  0
  •   Jez    16 年前

    !这是我找到的最好的解决办法!

    好的,各位,很抱歉这里的答案是垃圾邮件,但我发现了一种更优雅的方法,可以避免ItemList需要使用“items”属性访问其项;使项目列表本身成为一个列表!这样,您只需直接以列表的形式访问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; }
        }
    }
    
        6
  •  0
  •   Jez    16 年前

    更新:请看答案开始!这是我找到的最好的解决方案这是一个比这个更好的解决方案。

    ...

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

    谢谢大家的帮助!