【问题标题】:Using .Net what limitations (if any) are there in using the XmlSerializer?使用 .Net 使用 XmlSerializer 有哪些限制(如果有)?
【发布时间】:2008-09-20 20:45:39
【问题描述】:

使用 .Net 使用 XmlSerializer 有哪些限制(如果有)? 比如,你能把Image序列化成XML吗?

【问题讨论】:

  • 请重新标记。这不是特定于 C# 的问题。改为使用“.net”标记。

标签: .net xml serialization


【解决方案1】:

我通常发现 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[]
  • 还有其他奇怪的边缘情况

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

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

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

  • 需要阅读 .InnerException 以获取有关错误的任何有用信息

【讨论】:

  • 天哪,太多了!的限制!感谢您的详细信息。 +1 票
  • XmlSerializer 需要一个无参数构造函数,但它不需要是公共的。内部足够好。不幸的是 BinaryFormatter.Serialize 和 .Deserialize 现在已被弃用并被视为安全风险。这是非常不幸的。
  • 你说...“需要知道所有类型”是什么意思?请指教。
【解决方案2】:

XmlSerializer 有一些缺点。

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

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

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

【讨论】:

  • 我认为,有了 3.5SP1,你甚至不需要再用 DataContract/DataMember 来装饰了。
  • +1:NetDataContractSerializer - 这对我来说是新的。谢谢!
【解决方案3】:

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

在app.config中加入下面几行就可以看代码了:

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

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

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

Look here for more information.

【讨论】:

    【解决方案4】:

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

    【讨论】:

    • 为什么有人反对这个?我写的windows服务已经被这个XMLSerializer内存泄露给拖累了,恕我直言。
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-01-06
            • 2011-02-12
            • 2016-03-01
            • 2021-08-25
            • 2014-11-27
            • 2014-03-19
            • 1970-01-01
            相关资源
            最近更新 更多