A型泛型列表反序列化类的? [英] A type of generic list deserialization class?

查看:184
本文介绍了A型泛型列表反序列化类的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

OK,所以这里的故事至今。

OK, so here's the story so far.

我已经可以使用反序列化单个对象的XmlSerializer ,但反序列化列表被证明是一个真正的头痛。我开始试图通过序列化列表<富> 和串行连载多个<富> XML内部结构根< ArrayOfFoo> 元素。这被证明是有问题的反序列化,所以看起来我需要有自己定义的ArrayOfFoo'元素。所以,我有一个一流的工作是一个包装的列表,如在此程序:

I could already deserialize individual objects using XmlSerializer, but deserializing lists was proving to be a real headache. I started out by trying to serialize List<Foo> and the serializer serialized multiple <Foo> XML structures inside a root <ArrayOfFoo> element. That proved to be problematic to deserialize, so it looks like I needed to have defined the 'ArrayOfFoo' element myself. So, I've got a class working that is a 'wrapper' for the list, as shown in this program:

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 ,仍具有在它已经手动定义的所有可能包含的元素(在这个例子中,个人和帐户)。什么是真正的理想是有一个泛型列表包装类。我知道这是一个有点乐观,但会有办法做到这一点?我在想的东西沿着这些路线(这并不工作,但只是给你的总体思路):

However, this 'wrapper', ItemList, still has to have manually defined in it all the elements that might be contained (in the example, Person and Account). What would be really ideal would be to have a generic list wrapper class. I know this is a bit hopeful, but would there be a way to do this? I'm thinking of something along these lines (this does not work, but is just to give you the general idea):

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结构 ITEMLIST 将只能在这个例子中一种类型,比如,我可以定义一个 ITEMLIST<&人GT; ,让我反序列化包含多个Person对象的列表?有任何想法吗?如果有必要,我不会介意来标记 ITEMLIST 与类 [XmlInclude ...] 每键入 ITEMLIST 可能包含的集合。

So, the XML structures passed inside the ItemList would only be able to be of one type, say Person in this example, and I could define an ItemList<Person> that would allow me to deserialize a list containing multiple Person objects? Any ideas? If necessary, I wouldn't mind having to tag the ItemList class with an [XmlInclude...] for every type that ItemList might contain a collection of.

我猜这是可能的,我只是还没有想通了相当如何? :-)或者是默认的XmlSerializer强求

I'm guessing this is possible, I just haven't figured out quite how? :-) Or is the default XmlSerializer too fussy?

推荐答案

更新:?!请看到答案开始本是最好的解决办法,我已经找到! - 这是一个比这更好的解决办法。

UPDATE: Please see the answer beginning !THIS IS THE BEST SOLUTION I'VE FOUND! - it's a better solution than this one.

...

重由csharptest.net的评论启发,我创建了一个类几乎没有这份工作,我想这一点。 :-)你通过检查ItemList.Items访问反序列化的物品,并通过将物品放入ItemList.Items然后用适当的XmlSerializer序列它序列东西。只有轻微的烦恼是,你必须确保ITEMLIST类被标为每类类型的XmlIncludeAttribute这可能需要(德)序列化,或XmlSerializer的将无法处理它。

Heavily inspired by csharptest.net's comment, I've created a class that pretty much does the job I wanted. :-) You access the deserialized items by checking ItemList.Items, and serialize stuff by inserting the items into ItemList.Items and then serializing it using an appropriate XmlSerializer. The only slight annoyance is that you must ensure that the ItemList class is tagged with an XmlIncludeAttribute for every class type that may need to be (de)serialized, or the XmlSerializer won't be able to deal with it.

下面的示例程序,包含通用ITEMLIST类:

Here's the example program, containing the generic ItemList class:

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



谢谢大家对你的帮助!

Thanks everyone for your help!

这篇关于A型泛型列表反序列化类的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆