指定XmlRootAttribute时XmlSerializer性能问题

我目前有一个非常奇怪的问题,我似乎无法弄清楚如何解决它。

我有一个相当复杂的类型,我正在尝试使用XmlSerializer类进行序列化。 这实际上运行正常,类型序列化正确,但似乎需要长时间这样做; 大约5秒,具体取决于对象中的数据。

经过一些分析后,我把问题缩小了 – 奇怪的是 – 在调用XmlSerializer.Serialize时指定一个XmlRootAttribute。 我这样做是为了将从ArrayOf序列化的集合的名称更改为更有意义的东西。 一旦我删除参数,操作几乎是即时的!

任何想法或建议都会非常好,因为我完全被这个问题所困扰!

只是遇到这个问题的其他人; 有了上面的答案和MSDN中的示例我设法使用以下类解决此问题:

public static class XmlSerializerCache { private static readonly Dictionary cache = new Dictionary(); public static XmlSerializer Create(Type type, XmlRootAttribute root) { var key = String.Format( CultureInfo.InvariantCulture, "{0}:{1}", type, root.ElementName); if (!cache.ContainsKey(key)) { cache.Add(key, new XmlSerializer(type, root)); } return cache[key]; } } 

然后,而不是使用默认的XmlSerializer构造函数,它采用XmlRootAttribute,我使用以下代码:

 var xmlRootAttribute = new XmlRootAttribute("ExampleElement"); var serializer = XmlSerializerCache.Create(target.GetType(), xmlRootAttribute); 

我的应用程序现在再次执行!

正如对原始问题的后续评论中所提到的,.NET在创建XmlSerializers时会发出程序集,如果使用以下两个构造函数之一创建生成的程序集,则会缓存生成的程序集:

 XmlSerializer(Type) XmlSerializer(Type, String) 

使用其他构造函数生成的程序集不会被缓存,因此.NET每次都必须生成新的程序集。

为什么? 这个答案可能不是很令人满意,但是在Reflector中对此TempAssemblyCacheKey ,您可以看到用于存储和访问生成的XmlSerializer程序集的密钥( TempAssemblyCacheKey )只是一个从可序列化类型构建的简单复合键,并且(可选)命名空间。

因此,没有机制来判断SomeType的缓存XmlSerializer是否具有特殊的XmlRootAttribute或默认值。

很难想到密钥无法容纳更多元素的技术原因,所以这可能只是一个没有人有时间实现的function(特别是因为它涉及改变其他稳定的类)。

您可能已经看过这个,但是如果还没有, XmlSerializer类文档讨论了一种解决方法:

如果使用任何其他构造函数,则会生成同一程序集的多个版本,并且永远不会卸载,这会导致内存泄漏和性能下降。 最简单的解决方案是使用前面提到的两个构造函数之一。 否则,必须将程序集缓存在Hashtable,如以下示例所示。

(我在这里省略了这个例子)

只需要实现这样的东西,并使用一个稍微更优化的@ Dougc解决方案版本,带来方便的过载:

 public static class XmlSerializerCache { private static readonly Dictionary cache = new Dictionary(); public static XmlSerializer Get(Type type, XmlRootAttribute root) { var key = String.Format("{0}:{1}", type, root.ElementName); XmlSerializer ser; if (!cache.TryGetValue(key, out ser)) { ser = new XmlSerializer(type, root); cache.Add(key, ser); } return ser; } public static XmlSerializer Get(Type type, string root) { return Get(type, new XmlRootAttribute(root)); } } 

这里解释了更复杂的实现。 但是该项目不再有效。

这里可以看到相关的类: http : //mvpxml.codeplex.com/SourceControl/changeset/view/64156#258382

特别是,以下生成唯一键的函数可能很有用:

 public static string MakeKey(Type type , XmlAttributeOverrides overrides , Type[] types , XmlRootAttribute root , String defaultNamespace) { StringBuilder keyBuilder = new StringBuilder(); keyBuilder.Append(type.FullName); keyBuilder.Append("??"); keyBuilder.Append(SignatureExtractor.GetOverridesSignature(overrides)); keyBuilder.Append("??"); keyBuilder.Append(SignatureExtractor.GetTypeArraySignature(types)); keyBuilder.Append("??"); keyBuilder.Append(SignatureExtractor.GetXmlRootSignature(root)); keyBuilder.Append("??"); keyBuilder.Append(SignatureExtractor.GetDefaultNamespaceSignature(defaultNamespace)); return keyBuilder.ToString(); }