8

使用 .Net 使用 XmlSerializer 有哪些限制(如果有)?例如,您可以将图像序列化为 XML 吗?

4

8 回答 8

23

我通常发现 XmlSerializer 对于任何不仅仅是 DTO 的 POCO 来说都是一个糟糕的选择。如果您需要特定的 XML,您可以使用 Xml*Attribute 和/或 IXmlSerializable 路线 - 但您会得到一个非常混乱的对象。

出于某些目的,它仍然是一个显而易见的选择——即使它有局限性。但是,为了简单地存储和重新加载数据,我发现 BinaryFormatter 是一个更容易的选择,而且陷阱更少。

以下是 XmlSerializer 的一些烦恼列表 - 大多数我都曾在某个时候或另一个地方被咬过,还有一些我在MSDN上找到的:

  • 需要一个公共的,没有参数的构造函数
  • 仅序列化公共读/写属性和字段
  • 需要知道所有类型
  • 实际上调用 get_* 和 set_*,因此将运行验证等。这可能是好是坏(还要考虑调用的顺序)
  • 只会序列化符合特定规则的 IEnumerable 或 ICollection 集合

XmlSerializer 对实现 IEnumerable 或 ICollection 的类进行特殊处理。实现 IEnumerable 的类必须实现采用单个参数的公共 Add 方法。Add 方法的参数必须与从 GetEnumerator 返回的值的 Current 属性返回的类型相同,或者该类型的基数之一。

除了 IEnumerable 之外,实现 ICollection 的类(例如 CollectionBase)必须具有采用整数的公共 Item 索引属性(C# 中的索引器),并且必须具有整数类型的公共 Count 属性。Add 方法的参数必须与从 Item 属性返回的类型相同,或者是该类型的基数之一。对于实现 ICollection 的类,要序列化的值是从索引的 Item 属性中检索的,而不是通过调用 GetEnumerator。

  • 不序列化 IDictionary
  • 使用动态生成的程序集,这些程序集可能不会从应用程序域中卸载。

为了提高性能,XML 序列化基础结构动态生成程序集以序列化和反序列化指定类型。基础结构查找并重用这些程序集。此行为仅在使用以下构造函数时发生:

XmlSerializer.XmlSerializer(类型) XmlSerializer.XmlSerializer(类型,字符串)

如果您使用任何其他构造函数,则会生成同一程序集的多个版本并且永远不会卸载,这会导致内存泄漏和性能下降。

  • 无法序列化 ArrayList[] 或 List<T>[]
  • 还有其他奇怪的边缘情况

如果满足以下条件,则无法实例化 XmlSerializer 以序列化枚举:枚举的类型为 unsigned long(C# 中的 ulong),并且枚举包含值大于 9,223,372,036,854,775,807 的任何成员。

XmlSerializer 类不再序列化标记为 [Obsolete] 的对象。

您必须有权写入临时目录(由 TEMP 环境变量定义)才能反序列化对象。

  • 需要阅读 .InnerException 以获取有关错误的任何有用信息
于 2008-09-21T00:26:12.747 回答
19

XmlSerializer 有一些缺点。

  1. 它必须知道所有被序列化的类型。您不能通过表示序列化程序不知道的类型的接口传递一些东西。
  2. 它不能做循环引用。
  3. 如果在对象图中多次引用,它将多次序列化同一个对象。
  4. 无法处理私有字段序列化。

我(愚蠢地)编写了自己的序列化程序来解决其中的一些问题。不要那样做;这是很多工作,你会在几个月后发现其中的细微错误。在编写自己的序列化程序和格式化程序时,我获得的唯一收获是对对象图序列化所涉及的细节有了更大的了解。

当 WCF 出现时,我发现了NetDataContractSerializer 。它完成了上面 XmlSerializer 没有做的所有事情。它以与 XmlSerializer 类似的方式驱动序列化。一种是用属性装饰各种属性或字段,以通知序列化程序要序列化什么。我用 NetDataContractSerializer 替换了我编写的自定义序列化程序,并且对结果非常满意。我会极力推荐它。

于 2008-09-20T21:07:10.040 回答
3

另一个问题是,调用 XmlSerializer 的构造函数将在运行时编译代码,并将生成一个临时 DLL(在 %temp% 文件夹中),其中包含执行反序列化的代码。

如果您在 app.config 中添加以下行,您可以观看代码:

  <system.diagnostics>
    <switches>
      <add name="XmlSerialization.Compilation" value="4"/>
    </switches>
  </system.diagnostics>

当您第一次序列化一个类并且需要具有编译和写入磁盘权限的代码时,这会花费大量时间。

一种解决方法是使用 VS 2005+ 附带的 sGen.exe 工具预编译这些 DLL。

在这里查看更多信息

于 2008-11-27T11:34:21.663 回答
2

不确定是否有任何限制.. 但是 .NET 1.1 中的 XmlSerialization 中存在内存泄漏错误,您必须创建一个缓存序列化程序对象来解决这个问题......事实上,我不确定这个问题已在 .net 2.0 或更高版本中修复...

于 2008-09-20T20:51:24.790 回答
1

我能想到的一个限制是 XmlSerialization 是可选的;意味着您不想序列化的类的任何属性都必须用 [XmlIgnore] 装饰。与所有属性都可选择加入的 DataContractSerializer 相比,您必须显式声明包含属性。这是一篇很好的文章

图像或其二进制数组由 XmlSerializer 序列化为 base64 编码文本。

于 2008-09-20T20:51:37.630 回答
1

理论上,您编写的任何类都可以通过 XmlSerializer 提供。但是,它只能访问公共字段,并且需要用正确的属性(例如 XmlAttribute)标记类。即使在基本框架中,也不是所有东西都支持 XmlSerializer。例如 System.Collections.Generic.Dictionary<>。

于 2008-09-20T20:55:11.567 回答
1

例如,您不能序列化实现 IDictionary 接口的类。

于 2008-09-20T21:17:38.097 回答
0

对于集合,他们需要一个带有单个参数的 Add 方法。如果您只需要文本格式而不是特定的 xml,则可以尝试 JSON。我已经为 .NET 开发了一个JsonExSerializer ,在http://www.json.org上还有其他可用的。

于 2008-09-21T00:17:19.943 回答