XML序列化:抽象属性反序列化的问题
本文关键字:反序列化 问题 属性 抽象 序列化 XML | 更新日期: 2023-09-27 18:02:21
我还在努力把我的大脑包裹在整个xml序列化的事情上,看来我又需要一些帮助了。
我需要能够反序列化抽象类型的属性。随着时间的推移,该类型将有许多不同的具体类型被添加,并且在许多不同的模型中被引用,因此明确列出每个具体类型并不是理想的解决方案。
我已经阅读了XML序列化和继承类型线程,并提出了以下内容:
<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)中有一个错误"。在前面提到的线程中,我唯一改变的是类名。
我的思路对吗?如果是这样,我做错了什么导致了这个错误?
从你的例子:
//[XmlElement(typeof(PlayElement))] **NOTE: the example works if I use this instead
这是支持抽象类的推荐方式。您可以根据元素名称切换实例,并使用如下的多个声明:
[XmlElement("playElement", typeof(PlayElement))]
[XmlElement("testElement", typeof(TestElement))]
public AbstractCommandModel Command;
当然,您仍然需要删除"introCommand"元素或添加另一个类来嵌套上述声明。
…
如果您仍然需要手动执行序列化,那么您就在正确的路径上。你的例子工作得很好,我猜,这是XML输出:
<Page>
<introCommand type="TestService.PlayElement, TestService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<PlayElement />
</introCommand>
</Page>
这个xml可以很好地读到你的对象;然而,这带来了安全问题。任何有权修改该XML或将其注入应用程序的人都可以轻松地注入代码。
以下代码用于测试示例代码:
private static void Main()
{
StringWriter dataOut = new StringWriter();
XmlTextWriter writer = new XmlTextWriter(dataOut);
writer.Formatting = Formatting.Indented;
Page p = new Page();
p.introCommand = new PlayElement();
new XmlSerializer(typeof(Page)).Serialize(writer, p);
string xml = dataOut.ToString();
Console.WriteLine(xml);
XmlTextReader reader = new XmlTextReader(new StringReader(xml));
Page copy = (Page) new XmlSerializer(typeof (Page)).Deserialize(reader);
}