继我之前的问题之后,我一直致力于将我的对象模型序列化为XML.但我现在遇到了一个问题(quelle surprise!).
我遇到的问题是我有一个集合,它是一个抽象的基类类型,由具体的派生类型填充.
我认为只需将XML属性添加到所涉及的所有类中,一切都会很好.可悲的是,事实并非如此!
所以我在谷歌上做了一些挖掘,现在我明白为什么它不起作用了.在该XmlSerializer
其实是在为了序列化对象做一些聪明的反射/通过XML,并且因为它的基础上,抽象类型,它不能弄清楚到底是怎么回事了它的交谈.精细.
我确实遇到过CodeProject 上的这个页面,它看起来好像很有帮助(还没有完全阅读/消费),但我想我也想把这个问题带到StackOverflow表中,看看你是否有任何整洁黑客/技巧,以最快/最轻的方式启动和运行.
有一件事我还要补充的是,我不要想往下走XmlInclude
的路线.与它有太多的耦合,系统的这个区域正在大量开发,所以这将是一个真正的维护头痛!
好的,所以我终于到了那里(诚然,从这里获得了很多帮助!).
总结一下:
由于头痛,我不想走XmlInclude路线.
一旦找到解决方案,我希望它能够在其他应用程序中快速实现.
可以使用抽象类型的集合,以及单独的抽象属性.
我真的不想在具体课上做"特殊"的事情.
XmlSerializer做了一些非常酷的反思,但它在抽象类型方面非常有限(即它只适用于抽象类型本身的实例,而不是子类).
Xml属性装饰器定义XmlSerializer如何处理它找到的属性.也可以指定物理类型,但这会在类和序列化器之间产生紧密耦合(不好).
我们可以通过创建一个实现IXmlSerializable的类来实现我们自己的XmlSerializer .
我创建了一个泛型类,您可以在其中指定泛型类型作为您将使用的抽象类型.这使得类能够在抽象类型和具体类型之间"转换",因为我们可以对转换进行硬编码(即我们可以获得比XmlSerializer更多的信息).
然后我实现了IXmlSerializable接口,这非常简单,但是在序列化时我们需要确保我们将具体类的类型写入XML,因此我们可以在反序列化时将其转换回来.同样重要的是要注意它必须是完全限定的,因为这两个类所在的程序集可能不同.当然有一些类型检查和需要在这里发生的事情.
由于XmlSerializer无法强制转换,我们需要提供代码来执行此操作,因此隐式运算符会被重载(我甚至不知道你可以这样做!).
AbstractXmlSerializer的代码是这样的:
using System; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; namespace Utility.Xml { public class AbstractXmlSerializer: IXmlSerializable { // Override the Implicit Conversions Since the XmlSerializer // Casts to/from the required types implicitly. public static implicit operator AbstractType(AbstractXmlSerializer o) { return o.Data; } public static implicit operator AbstractXmlSerializer (AbstractType o) { return o == null ? null : new AbstractXmlSerializer (o); } private AbstractType _data; /// /// [Concrete] Data to be stored/is stored as XML. /// public AbstractType Data { get { return _data; } set { _data = value; } } ////// **DO NOT USE** This is only added to enable XML Serialization. /// ///DO NOT USE THIS CONSTRUCTOR public AbstractXmlSerializer() { // Default Ctor (Required for Xml Serialization - DO NOT USE) } ////// Initialises the Serializer to work with the given data. /// /// Concrete Object of the AbstractType Specified. 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属性类型属性中传递我们的类型,例如:
[XmlRoot("ClassWithAbstractCollection")] public class ClassWithAbstractCollection { private List_list; [XmlArray("ListItems")] [XmlArrayItem("ListItem", Type = typeof(AbstractXmlSerializer ))] public List List { get { return _list; } set { _list = value; } } private AbstractType _prop; [XmlElement("MyProperty", Type=typeof(AbstractXmlSerializer ))] public AbstractType MyProperty { get { return _prop; } set { _prop = value; } } public ClassWithAbstractCollection() { _list = new List (); } }
在这里你可以看到,我们有一个集合和一个属性被暴露,我们需要做的就是将名为parameter 的类型添加到Xml声明中,这很容易!:d
注意:如果您使用此代码,我将非常感谢您的支持.它还将有助于吸引更多人加入社区:)
现在,但不确定这里的答案是什么,因为他们都有他们的专业和骗子.我会upmod那些我觉得有用的东西(对那些没有的东西没有冒犯)并且一旦我有了代表关闭它:)
有趣的问题和很好的解决方案!:)
要注意的一件事是,在XmlSerialiser构造函数中,您可以传递序列化程序可能难以解析的类型数组.我不得不使用那几次需要序列化的集合或复杂的数据结构集,并且这些类型存在于不同的程序集等中.
带有extraTypes参数的XmlSerialiser构造函数
编辑:我想补充说,这种方法有利于XmlInclude属性等,你可以找到一种方法,在运行时发现和编译你可能的具体类型的列表,并填充它们.