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

但是,我无法结合这两个功能-如以下最小示例所示:

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

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



通过动态生成的XML程序集,我们最终通过调用以下代码回到.NET标准库代码:
System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(
     String, String, Object, Boolean) : Void

反过来,这导致:
protected Exception CreateUnknownTypeException(Type type)
{
    if (typeof(IXmlSerializable).IsAssignableFrom(type))
    {
        return new InvalidOperationException(
            Res.GetString("XmlInvalidSerializable",
            new object[] { type.FullName }));
    }

    // Rest omitted...

Reflector显示XmlInvalidSerializable资源与上面的字符串相对应-即WriteTypedPrimitive不喜欢IXmlSerializable

如果我们生成非多态序列化器,如下所示:
XmlSerializer xs = new XmlSerializer(typeof(CustomSerializable));

.NET将生成对以下内容的调用:
System.Xml.Serialization.XmlSerializationWriter.WriteSerializable(
    IXmlSerializable, String, String, Boolean) : Void

这样可以正确处理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);
}

是出于技术原因还是仅出于功能限制而将其排除在外?不支持的功能,还是我的愚蠢?我在intertubes上的Google技能使我失望。

我确实在这里找到了一些相关的问题,其中最重要的是“C# Xml-Serializing a derived class using IXmlSerializable”。它使我相信这是根本不可能的。

在那种情况下,我目前的想法是在根基类中注入(inject)默认的IXmlSerializable实现。然后,所有内容将都是IXmlSerializable,.NET不会提示。我可以使用Reflection.Emit鞭打每种具体类型的ReadXmlWriteXml主体,生成与我使用库一相同的XML。

有些人在遇到XML序列化问题时会想到“我知道,我将使用Reflection.Emit生成代码。”现在他们有两个问题。

P.S.笔记;我知道.NET XML序列化的替代方法,并且知道它有局限性。我还知道,保存POCO比处理抽象数据类型要简单得多。但是我有一堆旧代码,并且需要对现有XML模式的支持。

因此,尽管我很高兴看到答复显示SomeOtherXMLYAMLXAMLProtocolBuffersDataContractRandomJsonLibraryThrift或您的MorseCodeBasedSerializeToMp3库是多么容易-嘿,我可能会学到一些东西-我希望的是XML序列化器的工作-周围,​​如果不能解决。

最佳答案

使用object时,我能够重现您的问题:

XmlSerializer xs = new XmlSerializer(
    typeof(object),
    new Type[] { typeof(CustomSerializable) });

但是,然后我创建了CustomSerializable的派生类:
public class CustomSerializableDerived : CustomSerializable
{
}

并尝试对其进行序列化:
XmlSerializer xs = new XmlSerializer(
    typeof(CustomSerializable),
    new Type[] { typeof(CustomSerializableDerived) });

这行得通。

因此,问题似乎仅限于您将“object”指定为要序列化的类型的情况,但如果您指定了具体的基本类型,则情况并非如此。

早上,我将对此进行更多研究。

关于c# - 多态类型和IXmlSerializable,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/631852/

10-11 13:05