多态类型和IXmlSerializable

Jaa*_*ter 9 c# xml-serialization

.NET中的XML序列化允许通过构造函数的extraTypes[]参数进行多态对象XmlSerializer.它还允许为实现的类型定制XML序列化IXmlSerializable.

但是,我无法将这两个功能结合起来 - 正如这个最小的例子所示:

using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace CsFoo
{
    public class CustomSerializable : IXmlSerializable
    {
        public XmlSchema GetSchema() { return null; }
        public void ReadXml(XmlReader xr) { }
        public void WriteXml(XmlWriter xw) { }
    }

    class CsFoo
    {
        static void Main()
        {
            XmlSerializer xs = new XmlSerializer(
                typeof(object),
                new Type[] { typeof(CustomSerializable) });

        xs.Serialize(new StringWriter(), new CustomSerializable());
    }
}
Run Code Online (Sandbox Code Playgroud)

最后一行抛出System.InvalidOperationException此消息:

在此上下文中可能不使用类型CsFoo.CustomSerializable来使用C​​sFoo.CustomSerializable作为参数,返回类型或类或结构的成员,参数,返回类型或成员必须声明为类型CsFoo.CustomSerializable(它不能是对象).CsFoo.CustomSerializable类型的对象不能用于未类型的集合,例如ArrayLists.

通过动态生成的XML程序集,我们最终通过调用以回到.NET标准库代码:

System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(
     String, String, Object, Boolean) : Void
Run Code Online (Sandbox Code Playgroud)

反过来,这会导致:

protected Exception CreateUnknownTypeException(Type type)
{
    if (typeof(IXmlSerializable).IsAssignableFrom(type))
    {
        return new InvalidOperationException(
            Res.GetString("XmlInvalidSerializable",
            new object[] { type.FullName }));
    }

    // Rest omitted...
Run Code Online (Sandbox Code Playgroud)

反射器显示XmlInvalidSerializable资源对应于上面的字符串 - 即, WriteTypedPrimitive不喜欢IXmlSerializable.

如果我们生成一个非多态的序列化器,如下所示:

XmlSerializer xs = new XmlSerializer(typeof(CustomSerializable));
Run Code Online (Sandbox Code Playgroud)

.NET将生成一个调用:

System.Xml.Serialization.XmlSerializationWriter.WriteSerializable(
    IXmlSerializable, String, String, Boolean) : Void 
Run Code Online (Sandbox Code Playgroud)

这处理IXmlSerializable得当.有谁知道为什么.NET在多态情况下不使用这个函数?查看XML序列化程序生成的C#,在我看来,这可以很容易地完成.这是我从XML序列化器获得的一些代码,带有未经测试的解决方案:

void Write1_Object(string n, string ns, global::System.Object o, 
   bool isNullable, bool needType)
{
    if ((object)o == null)
    {
        if (isNullable) WriteNullTagLiteral(n, ns);
        return;
    }
    if (!needType)
    {
        System.Type t = o.GetType();
        if (t == typeof(global::System.Object))
        {
        }
>>> patch begin <<<
+         else if (typeof(IXmlSerializable).IsAssignableFrom(t))
+         {
+             WriteSerializable((System.Xml.Serialization.IXmlSerializable)
                   ((global::CsFoo.CustomSerializable)o), 
+                  @"CustomSerializable", @"", true, true);
+         }
>>> patch end <<<
        else
        {
            WriteTypedPrimitive(n, ns, o, true);
            return;
        }
    }
    WriteStartElement(n, ns, o, false, null);
    WriteEndElement(o);
}
Run Code Online (Sandbox Code Playgroud)

这是出于技术原因还是仅限于功能限制?不支持的功能,还是我的愚蠢?我的intertubes谷歌技能让我失望.

我确实在这里找到了一些相关的问题," C#Xml-使用IXmlSerializable序列化派生类 "是最相关的.这让我相信它根本不可能.

在这种情况下,我目前的想法是IXmlSerializable在根基类中注入默认实现.然后一切都会成为IXmlSerializable,.NET不会抱怨.我可以使用Reflection.Emit的鞭打了ReadXml,并WriteXml针对每个具体类型的机构,生成的XML将看起来是一样的,因为它会如果我使用的库之一.

有些人在面对XML序列化问题时会想"我知道,我会使用Reflection.Emit生成代码".现在他们有两个问题.


PS Note; 我知道.NET的XML序列化的替代品,并且知道它有局限性.我也知道保存POCO比处理抽象数据类型简单得多.但是我有一堆遗留代码,需要支持现有的XML模式.

因此,虽然我明白的答复,表明这是多么容易SomeOtherXML,YAML,XAML,ProtocolBuffers,DataContract,RandomJsonLibrary,Thrift,或者你的MorseCodeBasedSerializeToMp3库-嘿,我可能会学到一些东西-什么我希望的是一个XML序列化的变通,如果不解决方案.

Joh*_*ers 2

使用以下命令时,我能够重现您的问题object

XmlSerializer xs = new XmlSerializer(
    typeof(object),
    new Type[] { typeof(CustomSerializable) });
Run Code Online (Sandbox Code Playgroud)

但是,我随后创建了一个派生类CustomSerializable

public class CustomSerializableDerived : CustomSerializable
{
}
Run Code Online (Sandbox Code Playgroud)

并尝试将其序列化:

XmlSerializer xs = new XmlSerializer(
    typeof(CustomSerializable),
    new Type[] { typeof(CustomSerializableDerived) });
Run Code Online (Sandbox Code Playgroud)

这有效。

因此,问题似乎仅限于您指定“对象”作为要序列化的类型的情况,但如果您指定具体的基本类型,则问题不存在。

早上我会对此做更多研究。