martes, 25 de mayo de 2010

XslCompiledTransform y codificaciones de caracteres.

Recientemente estaba depurando una aplicación en la que se utiliza System.Xml.Xsl.XslCompiledTransform para aplicar una hoja de estilo XSL a un archivo XML en .Net y me topé con que la aplicación de la directiva <xsl:output encoding="iso-8859-1" /> depende de la clase utilizada para serializar la salida.

Codificación de caracteres en una transformación XML

Dado que XML utiliza la UTF-8 como la codificación predeterminada, se puede utilizar cualquier caracter Unicode en los valores de los elementos (algunos caracteres como < y > se deben codificar con su notación de entidad XML: &lt; para < y &gt; para >). A través de la directiva <xsl:output ... /> se puede indicar la codificación de caracteres que debe tener el resultado de la transformación utilizando el atributo encoding. De esta manera se puede producir un documento con una codificación diferente a UTF-8 en la cual los caracteres que utilicen códigos superiores a 255 (0x00FF) se codifican con la notación para códigos Unicode: &#nnnn; para la notación decimal o &#xnnnn; para la notación hexadecimal.

Codificación de caracteres con un XmlWriter

Normalmente se utiliza código como el siguiente para obtener el resultado de una transformación en una variable tipo string en memoria:

//Crear instancia de StringBuilder para construir salida.
var sb = new StringBuilder();
//Crear instancia para la transformación.
var xsl = new XslCompiledTransform();
//Cargar hoja de estilo para la transformación.
xsl.Load("transformacion.xsl");
//Crear instancia de XmlWriter a partir del StringBuilder y la configuración de salida
//indicada en <xsl:output />
var xw = XmlWriter.Create(sb, xsl.OutputSettings);
//Aplicar transformación.
xsl.Transform("entrada.xml", xw);
//Obtener resultado.
var resultado = sb.ToString();

Si ejecutamos este código el resultado que obtenemos es la entrada transformada, pero simpre codificada en UTF-8 aún cuando se indique otra codificación en la directiva <xsl:ouput encoding="..." />. ¿Por qué? Resulta, que las variables de tipo string son cadenas de caracteres Unicode y la codificación predeterminada es UTF-8 y StringBuilder no permite indicar el tipo de codificación deseada.

Para poder obtener el resultado con la codificación correcta en lugar de utilizar StringBuilder debemos utilizar un MemoryStream para obtener el resultado como una serie de bytes:

var resultado = string.Empty;
using (var ms = new MemoryStream()) {
  //Crear instancia para la transformación.
  var xsl = new XslCompiledTransform();
  //Cargar hoja de estilo para la transformación.
  xsl.Load("transformacion.xsl");
  //Crear instancia de XmlWriter a partir del StringBuilder y la configuración de salida
  //indicada en <xsl:output />
  var xw = XmlWriter.Create(ms, xsl.OutputSettings);
  //Aplicar transformación.
  xsl.Transform("entrada.xml", xw);
  //Cerrar instancia de XmlWriter
  xw.Close();
  //Restablecer posición del MemoryStream.
  ms.Position = 0;
  //Crear una instancia de StreamReader a partir del MemoryStream.
  using (var sr = new StreamReader(ms))
    //Obtener resultado.
    resultado = sr.ReadToEnd();
}

De esta manera la cadena resultante respetará la codificación indicada en la directiva <xsl:output encoding="..." />. Espero que esta nota les ahorre tiempo y dolores de cabeza al depurar transformaciones XSL utilizando .Net Framework.

No hay comentarios:

Publicar un comentario