本文介绍了XML 序列化和继承类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

继我的上一个问题之后,我一直在努力获取我的对象模型序列化为 XML.但我现在遇到了一个问题(令人惊讶!).

Following on from my previous question I have been working on getting my object model to serialize to XML. But I have now run into a problem (quelle surprise!).

我的问题是我有一个集合,它是一个抽象基类类型,由具体的派生类型填充.

The problem I have is that I have a collection, which is of a abstract base class type, which is populated by the concrete derived types.

我认为只需将 XML 属性添加到所有涉及的类就可以了,一切都会好起来的.遗憾的是,事实并非如此!

I thought it would be fine to just add the XML attributes to all of the classes involved and everything would be peachy. Sadly, thats not the case!

所以我在 Google 上做了一些挖掘,现在我明白了为什么它不起作用.XmlSerializer 实际上做了一些巧妙的反射,以便将对象序列化到 XML 或从 XML 序列化对象,而且由于它基于抽象类型,它无法弄清楚它到底在说什么.很好.

So I have done some digging on Google and I now understand why it's not working. In that the XmlSerializer is in fact doing some clever reflection in order to serialize objects to/from XML, and since its based on the abstract type, it cannot figure out what the hell it's talking to. Fine.

我确实在 CodeProject 上遇到了这个页面,看起来可能很有帮助(还没有完全阅读/消费),但我想我也想把这个问题带到 StackOverflow 表,看看你是否有任何巧妙的技巧/技巧,以便以最快的速度启动和运行/最简单的方法.

I did come across this page on CodeProject, which looks like it may well help a lot (yet to read/consume fully), but I thought I would like to bring this problem to the StackOverflow table too, to see if you have any neat hacks/tricks in order to get this up and running in the quickest/lightest way possible.

我还应该补充的一件事是我不想想沿着 XmlInclude 路线走下去.与它的耦合实在是太多了,而且系统的这个区域正在大量开发中,所以这将是一个真正的维护难题!

One thing I should also add is that I DO NOT want to go down the XmlInclude route. There is simply too much coupling with it, and this area of the system is under heavy development, so the it would be a real maintenance headache!

推荐答案

问题已解决!

好的,所以我终于到了那里(诚然,在 很多帮助下noreferrer">这里!)

Problem Solved!

OK, so I finally got there (admittedly with a lot of help from here!).

总结:

  • 由于维护问题,我不想走 XmlInclude 路线.
  • 找到解决方案后,我希望它能够在其他应用程序中快速实施.
  • 可以使用抽象类型的集合以及单独的抽象属性.
  • 我真的不想费心去做特别"的事情具体类中的东西.
  • XmlSerializer 做了一些非常酷的反射,但是当涉及到抽象类型时它非常有限(即它只能处理抽象类型本身的实例,而不是子类).
  • Xml 属性装饰器定义 XmlSerializer 如何处理它找到的属性.也可以指定物理类型,但这会在类和序列化程序之间造成紧耦合(不好).
  • 我们可以通过创建一个实现 IXmlSerializable 的类来实现我们自己的 XmlSerializer.
  • XmlSerializer does some pretty cool reflection, but it is very limited when it comes to abstract types (i.e. it will only work with instances of the abstract type itself, not subclasses).
  • The Xml attribute decorators define how the XmlSerializer treats the properties its finds. The physical type can also be specified, but this creates a tight coupling between the class and the serializer (not good).
  • We can implement our own XmlSerializer by creating a class that implements IXmlSerializable .

我创建了一个泛型类,您可以在其中指定泛型类型作为您将使用的抽象类型.这使班级能够翻译"抽象类型和具体类型之间,因为我们可以对转换进行硬编码(即我们可以获得比 XmlSerializer 更多的信息).

I created a generic class, in which you specify the generic type as the abstract type you will be working with. This gives the class the ability to "translate" between the abstract type and the concrete type since we can hard-code the casting (i.e. we can get more info than the XmlSerializer can).

然后我实现了 IXmlSerializable 接口,这很简单,但是在序列化时我们需要确保我们将具体类的类型写入 XML,这样我们可以在 de- 序列化.同样重要的是要注意它必须完全限定,因为这两个类所在的程序集可能不同.当然,这里需要进行一些类型检查和其他事情.

I then implemented the IXmlSerializable interface, this is pretty straight forward, but when serializing we need to ensure we write the type of the concrete class to the XML, so we can cast it back when de-serializing. It is also important to note it must be fully qualified as the assemblies that the two classes are in are likely to differ. There is of course a little type checking and stuff that needs to happen here.

由于 XmlSerializer 无法强制转换,我们需要提供代码来执行此操作,因此隐式运算符随后会被重载(我什至不知道您可以这样做!).

Since the XmlSerializer cannot cast, we need to provide the code to do that, so the implicit operator is then overloaded (I never even knew you could do this!).

AbstractXmlSerializer 的代码是这样的:

The code for the AbstractXmlSerializer is this:

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 属性类型属性中传递我们的类型,例如:

So, from there, how do we tell the XmlSerializer to work with our serializer rather than the default? We must pass our type within the Xml attributes type property, for example:

[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

Here you can see, we have a collection and a single property being exposed, and all we need to do is add the type named parameter to the Xml declaration, easy! :D

注意:如果您使用此代码,我将非常感谢您的支持.它还有助于吸引更多人加入社区 :)

现在,但不确定如何处理这里的答案,因为他们都有自己的优点和缺点.我会修改我认为有用的那些(对那些没有用的没有冒犯),一旦我有代表就关闭它:)

Now, but unsure as to what to do with answers here since they all had their pro's and con's. I'll upmod those that I feel were useful (no offence to those that weren't) and close this off once I have the rep :)

有趣的问题,解决起来很有趣!:)

Interesting problem and good fun to solve! :)

这篇关于XML 序列化和继承类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 02:44