2009-10-07 40 views
17

我目前有一个非常奇怪的问题,我似乎无法弄清楚如何解决它。指定XmlRootAttribute时XmlSerializer性能问题

我有相当复杂类型,我试图序列化使用XmlSerializer类。这实际上运行正常,并且类型序列化正确,但似乎很长时间需要很长时间;大约5秒钟,取决于对象中的数据。

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

任何想法或建议都非常好,因为我完全沉迷于这一个!

+1

好的,看起来像问题是如果您为序列化程序指定了任何非类型参数,则会为每个序列化程序实例生成序列化程序集!这就是为什么 - 我认为 - 我看到如此糟糕的表现。 有谁知道为什么默认的XmlSerializer会这样做的任何原因?我不明白为什么只需指定根节点名称就意味着缓存无法使用? – Dougc 2009-10-09 17:32:36

回答

23

只为别人谁运行到这个问题;与上面的答案,从MSDN的例子武装我设法使用下面的类来解决这个问题:

public static class XmlSerializerCache 
{ 
    private static readonly Dictionary<string, XmlSerializer> cache = 
          new Dictionary<string, XmlSerializer>(); 

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

我的应用程序现在正在执行!

+0

+1,简短而甜美。 – 2009-10-11 12:37:56

+6

这是一个不小的优化。 TryGet if-clause中的值,如果你要做很多这些查找。如果您只想知道该项是否存在,但ContainsKey效率更高,但由于您始终返回该值,因此tryget更好:http://dotnetperls.com/dictionary-lookup – 2010-03-21 15:53:32

+0

然后,您还可以改进“返回“语句,因为你已经在变量中有实例,并且不需要通过”cache [key]“来查找 – Sielu 2016-09-26 07:19:06

18

如在后续评论原来的问题提到的,.NET创建XmlSerializers时发射组件,并且如果使用这两个构造函数之一创建它缓存生成的汇编:产生

XmlSerializer(Type) 
XmlSerializer(Type, String) 

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

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

因此,没有任何机制可以确定缓存的XmlSerializer对于SomeType是否有特殊的XmlRootAttribute或默认值。

很难想到技术上的原因,认为键不能容纳更多的元素,所以这可能只是一个没有人有时间实现的特性(尤其是因为它会涉及到改变稳定的类)。

你可能已经看到了这一点,但如果你没有,the XmlSerializer class documentation讨论了解决方法:

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

(我省略了这里的例子)

+0

非常好。谢谢你的帮助杰夫。我不知道为什么我没有阅读MSDN文档,doh! – Dougc 2009-10-11 10:58:48

+0

有没有这个HashTable的任何例子,你不能控制Xml串行器?我特别在WCF服务引用或Web引用中思考?我有这个问题,但我似乎找不到任何解决方法......! – harrisonmeister 2015-04-08 08:58:35

0

有一个更复杂的实现解释here。但是该项目不再活跃。

相关的类在这里看到: 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(); 
} 
1

就必须实现这样的事情,并使用了稍@ Dougc解决方案的更优化版本,带有方便的过载:

public static class XmlSerializerCache { 
    private static readonly Dictionary<string, XmlSerializer> cache = new Dictionary<string, XmlSerializer>(); 

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