【发布时间】:2011-05-14 07:21:23
【问题描述】:
.NET 中的 XML 序列化允许通过 XmlSerializer 构造函数的 extraTypes[] 参数实现多态对象。它还允许为实现 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 并带有此消息:
CsFoo.CustomSerializable 类型不能在此上下文中用于 使用 CsFoo.CustomSerializable 作为参数,返回 类型,或类或结构的成员,参数,返回 类型或成员必须声明为类型 CsFoo.CustomSerializable (它不能是对象)。 CsFoo.CustomSerializable 类型的对象 不能用于无类型集合,例如 ArrayLists。
遍历动态生成的 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”最为相关。这让我相信这根本不可能。
在这种情况下,我目前的想法是在根基类中注入一个默认的IXmlSerializable 实现。然后一切都将是IXmlSerializable,并且.NET 不会抱怨。我可以使用 Reflection.Emit 为每种具体类型生成 ReadXml 和 WriteXml 主体,生成的 XML 看起来与使用库时相同。
有些人在遇到 XML 序列化问题时会想“我知道,我会使用 Reflection.Emit 来生成代码”。现在他们有两个问题。
附:笔记;我知道 .NET 的 XML 序列化的替代方案,并且知道它有局限性。我也知道保存 POCO 比处理抽象数据类型要简单得多。但是我有一堆遗留代码,需要对现有 XML 模式的支持。
因此,虽然我很欣赏在SomeOtherXML、YAML、XAML、ProtocolBuffers、DataContract、RandomJsonLibrary、Thrift 或您的 MorseCodeBasedSerializeToMp3 库中显示这很容易的回复 -嘿,我可能会学到一些东西——我希望的是一个 XML 序列化程序的解决方法,如果不是解决方案的话。
【问题讨论】:
-
尽管添加的时间很晚,但无论如何:在 IXmlSerializable 的情况下,具有额外类型的解决方案对我不起作用。然而this approach 正在工作,因为它允许反序列化的类与属性类 (!) 不同,因此任何找到要创建的正确类型的自定义逻辑都可以通过这种方式实现。
-
还有另一个解决方案声明为here(使用
XmlSchemaProvider属性将xml 类型绑定到.net 类型),但它缺少代码,我无法实现这个想法。有人吗?
标签: c# xml-serialization