指定XmlRootAttribute时XmlSerializer性能问题

时间:2009-10-07 23:41:42

标签: c# .net xml performance xml-serialization

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

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

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

任何想法或建议都会很棒,因为我完全不知道这个!

4 个答案:

答案 0 :(得分:24)

只为遇到此问题的其他人;使用上面的答案和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);

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

答案 1 :(得分:18)

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

XmlSerializer(Type)
XmlSerializer(Type, String)

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

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

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

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

您可能已经看过这个,但是如果您没有看到,the XmlSerializer class documentation讨论了一种解决方法:

  

如果你使用其他任何一个   构造函数,多个版本的   生成相同的程序集,永远不会   卸载,这会产生一个记忆   泄漏和性能不佳。最简单的   解决方案是使用其中之一   前面提到了两个构造函数。   否则,你必须缓存   Hashtable,中的程序集,如图所示   以下示例。

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

答案 2 :(得分: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));
    }
}

答案 3 :(得分: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();
}