以下从我的 前一个问题 我已经工作得到我的对象模型化,XML。但我现在已经遇到的问题(quelle惊喜!).

我的问题是,我有一个收集,这是一个抽象的基本类型,其中填充的具体源的类型。

我以为这会是很好只是添加XML属性的所有类涉及所有的一切都将被桃色。可悲的是,这就是不对的情况下!

所以我做了一些挖掘在谷歌和我现在明白了 为什么 它不工作。在那 XmlSerializer 其实是做了一些聪明的反思,以便serialize对象/XML,因为其根据的抽象的类型,它不能找出什么它说话.罚款。

我有没有遇到 这页 上演示,它看起来像它可能有很大的帮助(尚未阅读/消耗是全部),但是我以为我想带这个问题的计算器表也看,如果你有任何整齐的黑客/技巧,以便获得这种建立和运行在最短/最轻的方式。

有一件事我还应该补充的是我 不不 想下去的 XmlInclude 路线。有太多的联接,以及这一领域的系统是在开发中,所以这将是一个真正维护头痛!

有帮助吗?

解决方案

问题解决了!

OK,因此我最后到了那里(应当承认与一个 很多 的帮助 在这里,!).

所以总结:

目标:

  • 我不想下去 XmlInclude 路线,由于维护头痛的问题。
  • 一旦解决,我希望它能快速实施在其他应用程序。
  • 集的抽象的类型可以使用,以及作为单独的抽象性质。
  • 我真的不想打扰不得不做"特殊"情况的具体课程。

确定问题/要点注意:

  • 序列化程序 做一些很酷的反映,但是它是 非常 有限的时候抽象的类型(即它只会的工作实例的抽象的类型本身,而不是子类).
  • Xml属装饰的定义如何在序列化程序将其性质的发现。物理的类型也可以规定,但这将创建一个 紧密联结 之类和程序(不良)。
  • 我们可以实现我们自己的序列化程序通过创建一个类实现了 IXmlSerializable .

该解决方案

我创建了一个通用的级,在其指定的一般类型作为抽象的类型将工作。这给人类的能力"翻译"之间的抽象的类型和具体型,因为我们可以硬代码的铸件(即我们可以得到更多的信息比序列化程序可)。

然后我实现的 IXmlSerializable 接口,这是相当直截了当,但是当将我们需要确保我们写这种类型的具体类XML,所以我们可以把它当回de-顺序.它也是重要的是要注意到它必须是 完全合格的 作为大会两个类别中都可能有所不同。当然还有一个小型检查和东西需要发生在这里。

因序列化程序不偏,我们需要提供的代码这样做,所以隐含的操作者,然后载的(我从不知道你能做到!)。

代码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
    }
}

因此,从这里,我们怎么告诉的序列化程序的工作与我们的程序,而不是默认的?我们必须通过我们的类型在Xml属性类型的财产,例如:

[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

注:如果使用这个代码,我真的很欣赏一个叫喊。它还将帮助推动更多的人员到社区)

现在,但不确定怎么做的答案在这里,因为他们都有自己的利弊。我会upmod这些,我觉得是有用的(没有犯罪到那些没有)和附近的这一次我的代表:)

有趣的问题和良好的乐趣解决!:)

其他提示

一件事是事实,在XmlSerialiser构造你可以通过一系列类型的serialiser可能是有困难的解决。我已经用了好几次集合或复杂的数据结构需要序列化和这些类型住在不同的组件等。

XmlSerialiser构造与extraTypes param

编辑:我要补充说,这种做法的好处超过XmlInclude属性等等,你可以出去工作的一种方式发现和编制一个名单的可能的具体类型的运行和他们的东西。

说真的,一个可扩展的框架的POCOs将永远不会列化的XML可靠。我这样说是因为我可以保证有人会过来,扩大类,并把事情弄糟的。

你应该看到的反馈序列化对象的图表。它的设计是要做到这一点,而XML化不是。

Xaml程序和解串处理仿制药没有一个问题,集合基类和接口以及(只要收集本身实施 IListIDictionary).还有一些需要注意的,例如标记你的只读收集性与 DesignerSerializationAttribute, 但改造你的代码来处理这些情况不是那么困难。

只是一个快速更新关于这一点,我没有忘记!

只是做一些更多的研究,看起来像我上一个胜利者,只需要获得的代码分类。

迄今为止,我有以下几种:

  • XmlSeralizer 基本上是一种类,做一些漂亮的反射上课这是序列化.它确定性,这种属性化基础上的 类型.
  • 原因问题的发生是因为类型的不匹配现象的发生,它期待的 BaseType 但事实上接收 DerivedType ..虽然你可能会认为它会把它多态形式,它并不因为它将涉及整个额外负荷的反思和类型检查,这就不是设计来做。

这种行为似乎是能够被复盖(代码待定)通过创建一个代理类行动作为中间序列化程序。这基本上将确定类型的源类,然后serialize,作为正常的。此类代理然后将料,XML份行的主要程序..

看这个空间!^_^

这肯定是一个问题的解决方案,但还有另一个问题,这在一定程度上破坏了你的意到使用"便携式"的XML格式。糟糕的事情发生时你决定改变课程在下一个版本的程序和需要支持这两种格式化的--新的和老人(因为你的客户仍然使用他们的旧文件数据库,或者它们连接到你的服务器使用旧版本的产品)。但你不能使用这serializator了,因为你使用

type.AssemblyQualifiedName

它看起来像

TopNamespace.SubNameSpace.ContainingClass+NestedClass, MyAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089

这是包含大会属性和版本...

现在如果你试图改变你的装版本,或者决定签署,这反不是去工作...

我所做的事情相似,这一点。我通常做的是确保所有XML化属性的具体类的,只是有的特性在这类呼吁通过向基类(如需要)检索信息,这将是德/行化当程序的呼吁那些属性。它是一个比较编码的工作,但它的工作好多了,比试图强制程序只是做正确的事情。

更好的是,采用符号:

[XmlRoot]
public class MyClass {
    public abstract class MyAbstract {} 
    public class MyInherited : MyAbstract {} 
    [XmlArray(), XmlArrayItem(typeof(MyInherited))] 
    public MyAbstract[] Items {get; set; } 
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top