问题解决了!
好的,所以我终于到了那里(诚然,这里有很多帮助!)。
所以总结一下:
目标:
- 由于维护头痛,我不想走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
{
// 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 attributes 类型属性中传递我们的类型,例如:
[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>();
}
}
在这里你可以看到,我们有一个集合和一个被暴露的属性,我们需要做的就是将类型命名参数添加到 Xml 声明中,很简单!:D
注意:如果您使用此代码,我将非常感谢您的支持。这也将有助于推动更多的人加入社区 :)
现在,但不确定如何处理这里的答案,因为他们都有自己的优点和缺点。我会升级那些我觉得有用的东西(对那些没有用的人没有冒犯),一旦我有代表就关闭它:)
有趣的问题和解决的乐趣!:)