xmlelement serialize serializar deserialize deserializar c# .net xml performance xml-serialization

serialize - xml serialization c#



Problema de rendimiento de XmlSerializer al especificar XmlRootAttribute (4)

Actualmente tengo un problema realmente extraño y parece que no puedo encontrar la manera de resolverlo.

Tengo un tipo bastante complejo que estoy tratando de serializar usando la clase XmlSerializer. Esto realmente funciona bien y el tipo se serializa correctamente, pero parece tardar mucho tiempo en hacerlo; Alrededor de 5 segundos dependiendo de los datos en el objeto.

Después de un poco de perfilado, he reducido el problema, de manera extraña, a la especificación de un XmlRootAttribute al llamar a XmlSerializer.Serialize. Hago esto para cambiar el nombre de una colección que se está serializando desde ArrayOf a algo un poco más significativo. Una vez que elimine el parámetro la operación es casi instantánea!

¡Cualquier pensamiento o sugerencia sería excelente ya que estoy completamente perplejo con este!


Como se mencionó en el comentario de seguimiento a la pregunta original, .NET emite ensamblados al crear XmlSerializers y almacena en caché el ensamblado generado si se crea utilizando uno de estos dos constructores:

XmlSerializer(Type) XmlSerializer(Type, String)

Los ensamblajes generados utilizando los otros constructores no se almacenan en caché, por lo que .NET debe generar nuevos ensamblados cada vez.

¿Por qué? Esta respuesta probablemente no sea muy satisfactoria, pero al mirar esto en Reflector, puede ver que la clave utilizada para almacenar y acceder a los ensamblados XmlSerializer generados ( TempAssemblyCacheKey ) es solo una clave compuesta simple construida a partir del tipo serializable y (opcionalmente) su espacio de nombres.

Por lo tanto, no hay ningún mecanismo para saber si un XmlSerializer para XmlSerializer caché tiene un XmlRootAttribute especial o el predeterminado.

Es difícil pensar en una razón técnica por la que la clave no pueda acomodar más elementos, por lo que probablemente esta sea solo una característica que nadie tuvo tiempo de implementar (especialmente porque implicaría cambiar clases por lo demás estables).

Es posible que haya visto esto, pero en caso de que no lo haya hecho, la documentación de la clase XmlSerializer describe una solución alternativa:

Si utiliza alguno de los otros constructores, se generan varias versiones del mismo ensamblaje y nunca se descargan, lo que provoca una pérdida de memoria y un rendimiento deficiente. La solución más fácil es usar uno de los dos constructores mencionados anteriormente. De lo contrario, debe almacenar en caché los ensamblados en un Hashtable, como se muestra en el siguiente ejemplo.

(He omitido el ejemplo aquí)


Hay una implementación más compleja explicada here . Sin embargo el proyecto ya no está activo.

Las clases relevantes se pueden ver aquí: http://mvpxml.codeplex.com/SourceControl/changeset/view/64156#258382

En particular, la siguiente función para generar una clave única puede ser útil:

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


Solo para cualquier otra persona que se encuentre con este problema; armado con la respuesta anterior y el ejemplo de MSDN, logré resolver este problema usando la siguiente clase:

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

Luego, en lugar de usar el constructor de XmlSerializer predeterminado que toma un XmlRootAttribute, uso lo siguiente:

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

¡Mi aplicación ahora está funcionando otra vez!


Solo tuve que implementar algo como esto y usé una versión ligeramente más optimizada de la solución de @ Dougc con una sobrecarga de conveniencia:

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