Я все еще пытаюсь обернуть свой мозг вокруг всей этой сериализации XML, и кажется, что мне снова нужна помощь.
Мне нужно иметь возможность десериализовать свойство абстрактного типа. Этот тип будет иметь много разных конкретных типов, добавляемых с течением времени, и упоминается во многих разных моделях, поэтому явное перечисление каждого из конкретных типов не является идеальным решением.
Я прочитал поток XML Serialization and Inherited Types и придумал следующее:
<Page>
<introCommand>
<PlayElement />
</introCommand>
</Page>
**
namespace TestService
{
public class Page
{
[XmlElement("introCommand", Type = typeof(XmlCommandSerializer<AbstractCommandModel>))]
//[XmlElement(typeof(PlayElement))] **NOTE: the example works if I use this instead
public AbstractCommandModel introCommand;
}
}
**
namespace TestService
{
public class AbstractCommandModel
{
}
}
**
namespace TestService
{
public class PlayElement : AbstractCommandModel
{
}
}
**
namespace TestService
{
public class XmlCommandSerializer<AbstractCommandModel> : IXmlSerializable
{
// Override the Implicit Conversions Since the XmlSerializer
// Casts to/from the required types implicitly.
public static implicit operator AbstractCommandModel(XmlCommandSerializer<AbstractCommandModel> o)
{
return o.Data;
}
public static implicit operator XmlCommandSerializer<AbstractCommandModel>(AbstractCommandModel o)
{
return o == null ? null : new XmlCommandSerializer<AbstractCommandModel>(o);
}
private AbstractCommandModel _data;
/// <summary>
/// [Concrete] Data to be stored/is stored as XML.
/// </summary>
public AbstractCommandModel 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 XmlCommandSerializer()
{
// 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 AbstractCommandModel Specified.</param>
public XmlCommandSerializer(AbstractCommandModel 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(AbstractCommandModel).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(AbstractCommandModel).Name +
"' because the type specified in the XML was not found.");
// Check the Type is a Subclass of the AbstractCommandModel.
if (!type.IsSubclassOf(typeof(AbstractCommandModel)))
throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractCommandModel).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 = (AbstractCommandModel)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
}
}
Однако, когда я запускаю десериализатор, я получаю InvalidOperationException, в котором говорится: "В документе XML есть ошибка (3,3)". Единственное, что я изменил из примера в упомянутом выше потоке, это имена классов.
Я на правильном пути с этим? Если да, то что я испортил, чтобы вызвать эту ошибку?