问题已解决!
好了,我终于做到了(尽管在这里得到了很多帮助!)。
简要概述:
目标:
- 由于维护起来麻烦,我不想采用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<AbstractType> : IXmlSerializable
{
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;
public AbstractType Data
{
get { return _data; }
set { _data = value; }
}
public AbstractXmlSerializer()
{
}
public AbstractXmlSerializer(AbstractType data)
{
_data = data;
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
string typeAttrib = reader.GetAttribute("type");
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);
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.");
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 + "').");
reader.ReadStartElement();
this.Data = (AbstractType)new
XmlSerializer(type).Deserialize(reader);
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
Type type = _data.GetType();
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
new XmlSerializer(type).Serialize(writer, _data);
}
#endregion
}
}
那么,从这里开始,我们该如何告诉XmlSerializer使用我们的序列化程序而不是默认的呢?我们必须在Xml属性类型中传递我们的类型,例如:
[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>();
}
}
这里您可以看到,我们暴露了一个集合和一个单一属性,并且我们只需要添加名为type的参数到Xml声明中即可,非常简单!:D
注意:如果您使用此代码,我希望您能提及一下。这也将有助于吸引更多人加入社区 :)
现在,对于这里的答案,我不确定该怎么做,因为它们都有优点和缺点。我会赞同那些我认为有用的(无意冒犯那些没有用的),一旦我的声望足够高就会关闭这个问题 :)
有趣的问题,解决起来很有趣!:)