在上一个问题之后,我一直在努力使对象模型序列化为XML。但是我现在遇到了一个问题(奇怪的是!)。
我的问题是我有一个抽象基本类类型的集合,该集合由具体的派生类型填充。
我认为只将XML属性添加到所有涉及的类中就可以了,而且一切都会变得很顺利。可悲的是,事实并非如此!
因此,我在Google上进行了一些挖掘,现在我了解了 为什么 它不起作用。在 该XmlSerializer实际上是在做一些聪明的反射,以对象序列化到/从XML,并且因为它的基础上,抽象类型,它不能弄清楚到底是怎么回事了它的交谈。精细。
XmlSerializer
我确实在CodeProject上遇到过此页面,它看起来可能很有帮助(但仍可以完全阅读/使用),但我想我也想将此问题带到StackOverflow表中,以查看是否有任何整洁的地方骇客/技巧,以便以最快/最轻便的方式启动并运行。
有一件事我还要补充的是,我 不要 想往下走XmlInclude的路线。耦合太简单了,系统的这个区域正在大量开发中,因此这将是真正的维护难题!
XmlInclude
好的,所以我终于到达了那里(诚然,这里有 很多 帮助!)。
总结一下:
我创建了一个通用类,在其中您将通用类型指定为将要使用的抽象类型。这使该类能够在抽象类型和具体类型之间“转换”,因为我们可以对转换进行硬编码(即,与XmlSerializer相比,我们可以获得更多的信息)。
然后,我实现了 IXmlSerializable 接口,这非常简单,但是在序列化时,我们需要确保将具体类的类型写入XML,以便在反序列化时可以将其返回。同样重要的是要注意它必须 完全合格 ,因为这两个类所在的程序集可能会有所不同。当然,这里需要进行一些类型检查和操作。
由于XmlSerializer无法转换,因此我们需要提供代码来执行此操作,因此隐式运算符将被重载(我什至不知道您可以做到这一点!)。
AbstractXmlSerializer的代码是这样的:
using System; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; namespace Utility.Xml { public class AbstractXmlSerializer<AbstractType> : IXmlSerializable { // Override the Implicit Conversions Since the XmlSerializer // Casts to/from the required types implicitly. public static implicit operator AbstractType(AbstractXmlSerializer<AbstractType> o) { return o.Data; } public static implicit operator AbstractXmlSerializer<AbstractType>(AbstractType o) { return o == null ? null : new AbstractXmlSerializer<AbstractType>(o); } private AbstractType _data; /// <summary> /// [Concrete] Data to be stored/is stored as XML. /// </summary> public AbstractType Data { get { return _data; } set { _data = value; } } /// <summary> /// **DO NOT USE** This is only added to enable XML Serialization. /// </summary> /// <remarks>DO NOT USE THIS CONSTRUCTOR</remarks> public AbstractXmlSerializer() { // Default Ctor (Required for Xml Serialization - DO NOT USE) } /// <summary> /// Initialises the Serializer to work with the given data. /// </summary> /// <param name="data">Concrete Object of the AbstractType Specified.</param> public AbstractXmlSerializer(AbstractType data) { _data = data; } #region IXmlSerializable Members public System.Xml.Schema.XmlSchema GetSchema() { return null; // this is fine as schema is unknown. } public void ReadXml(System.Xml.XmlReader reader) { // Cast the Data back from the Abstract Type. string typeAttrib = reader.GetAttribute("type"); // Ensure the Type was Specified if (typeAttrib == null) throw new ArgumentNullException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name + "' because no 'type' attribute was specified in the XML."); Type type = Type.GetType(typeAttrib); // Check the Type is Found. if (type == null) throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name + "' because the type specified in the XML was not found."); // Check the Type is a Subclass of the AbstractType. if (!type.IsSubclassOf(typeof(AbstractType))) throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name + "' because the Type specified in the XML differs ('" + type.Name + "')."); // Read the Data, Deserializing based on the (now known) concrete type. reader.ReadStartElement(); this.Data = (AbstractType)new XmlSerializer(type).Deserialize(reader); reader.ReadEndElement(); } public void WriteXml(System.Xml.XmlWriter writer) { // Write the Type Name to the XML Element as an Attrib and Serialize Type type = _data.GetType(); // BugFix: Assembly must be FQN since Types can/are external to current. writer.WriteAttributeString("type", type.AssemblyQualifiedName); new XmlSerializer(type).Serialize(writer, _data); } #endregion } }
因此,从那里,我们如何告诉XmlSerializer使用我们的序列化程序而不是默认序列化程序?我们必须在Xml属性type属性中传递我们的类型,例如:
[XmlRoot("ClassWithAbstractCollection")] public class ClassWithAbstractCollection { private List<AbstractType> _list; [XmlArray("ListItems")] [XmlArrayItem("ListItem", Type = typeof(AbstractXmlSerializer<AbstractType>))] public List<AbstractType> List { get { return _list; } set { _list = value; } } private AbstractType _prop; [XmlElement("MyProperty", Type=typeof(AbstractXmlSerializer<AbstractType>))] public AbstractType MyProperty { get { return _prop; } set { _prop = value; } } public ClassWithAbstractCollection() { _list = new List<AbstractType>(); } }
在这里,您可以看到,我们有一个集合和一个单独的属性,我们要做的就是将名为parameter 的 类型 添加到Xml声明中,简单!:D
注意:如果您使用此代码,我将不胜感激。 它还将帮助吸引更多的人进入社区:)
现在,但是不确定在这里如何处理答案,因为他们都有自己的优点和缺点。我会升级那些我认为有用的代码(对那些没有用的代码没有任何冒犯),并在获得代表之后将其关闭:)
有趣的问题,很好解决的乐趣!:)