XML序列化:反序列化的问题抽象属性

XML序列化:反序列化的问题抽象属性

问题描述:

我现在还在环绕这整个XML序列化的事情我的大脑,而且似乎我需要一些帮助的又一次。

I am still trying to wrap my brain around this whole xml serialization thing, and it appears I am in need of some assistance yet again.

我需要能够反序列化抽象类型的属性。这种类型将被添加了许多不同的具体类型随着时间的推移,在许多不同的车型被引用,因此明确列出每个具体的类型是不是一个理想的解决方案。

I need to be able to deserialize a property of an abstract type. This type will have many different concrete types being added over time, and is referenced in a lot of different models, so explicitly listing each of the concrete types is not an ideal solution.

我已经阅读了 XML序列化和继承的类型线程,纷纷拿出有以下内容:

I have read the XML Serialization and Inherited Types thread, and have come up with the following:

<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)中的错误。的。我已经从前面提到的线程的例子唯一改变的是类的名字。

However, when I run the deserializer I get an InvalidOperationException stating "There is an error in XML document (3,3).". The only thing I have changed from the example in the before mentioned thread is the class names.

我是在正确的轨道上吗?如果是这样,我是怎么陷入困境导致此错误?

Am I on the right track with this? If so, what did I mess up to cause this error?

从你的例子:

//[XmlElement(typeof(PlayElement))] **NOTE: the example works if I use this instead

这是推荐的方式来支持抽象类。您可以通过元素名称切换实例,并使用多个声明是这样的:

This is the recommended way to support abstract classes. You can switch instances by element name and use multiple declarations like this:

[XmlElement("playElement", typeof(PlayElement))]
[XmlElement("testElement", typeof(TestElement))]
public AbstractCommandModel Command;

当然,你还需要去掉introCommand元素或添加其他类鸟巢上面的声明之中。

Of course you will still need to drop the "introCommand" element or add another class to nest the above declaration into.

...

如果您仍然需要手动序列化,那么你在正确的道路。你的榜样作品不够好我想,这里是XML输出​​:

If you still need to do the serialization manually then you are on the correct path. Your example works well enough I guess, here is the XML output:

<Page>
  <introCommand type="TestService.PlayElement, TestService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
    <PlayElement />
  </introCommand>
</Page>

和这个XML读就好到您的对象;然而,这presents一个安全问题。任何人都可以访问修改或注入该XML到您的应用程序可以轻松地将code。

And this xml reads just fine into your object; however, this presents a security concern. Anyone with access to modify or inject this XML into your application can easily inject code.

以下是用来测试你的例子code:

The following was used to test your example code:

    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);
    }