A pedido popular

He aquí un pequeño resumen de como hice para meter HTML dentro de XML y que saliese bien. Notese que dije 'pequeño resumen', voy a explicar como se hace para meter HTML en XML y que en la transformación XSL lo trate como HTML y no XML. Usaré como ejemplo el código que escribí para Caleio.

Como saben HTML y XML son derivados de SGML, ambos usan etiquetas y funcionan de manera similar, y ahí esta el problema. Al meter HTML en el XML el transformador XSL no sabe que es HTML y lo trata como XML normal. El problema salió cuando quise meter enlaces (o cualquier otro elemento HTML como imágenes o saltos de linea) en las descripciones de elementos. Simplemente no los mostraba porque intepretaba que eran etiquetas XML.

La solución es muy simple, pero me costó mucho encontrarla porque todo el mundo tiene un método distinto. Este consiste en indicar en el archivo XML cuales etiquetas son especiales, en este caso de HTML. Por ejemplo:

<?xml version="1.0" standalone="yes"?>
<event>
	<item>
		<id>325</id>
		<startDate>2005-08-16</startDate>
		<endDate>2005-08-16</endDate>
		<startTime>21:00:00</startTime>
		<endTime>22:00:00</endTime>
		<eventType>1</eventType>
		<scheduled>0</scheduled>
		<repetition>0</repetition>
		<title>testing</title>
		<description xmlns:html="http://www.w3.org/1999/xhtml">something with a <html:br />
<html:a href="http://www.google.com">link</html:a></description>
	</item>
</event>

La etiqueta description es la que lleva el HTML, el atributo xmlns lo reconocerán de la etiqueta html que se usa en XHTML. xmlns (http://www.w3.org/TR/REC-xml-names/) sirve para definir Namespaces en XML, los namespaces son definiciones específicas de etiquetas, como en XML una etiqueta no tiene significado alguno con namespaces se puede definir mas correctamente. Que es justo lo que hace xmlns en la etiqueta html en XHTML:

<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">

XHTML es XML, pero con el atributo xmlns le decimos al navegador que todo lo que hay dentro de la etiqueta html debe ser tratado como tal. Esto no es estrictamente necesario porque los navegadores lo detectan automaticamente, pero el validador de HTML tira error si se omite.

Aquí es donde entramos en terreno desconocido, algunas de las páginas que leí decian que con tal de poner el xmlns en la etiqueta que tuviese el HTML dentro ya bastaba, pero a mi se me hizo necesario poner :html para indicarle a XSL exactamente cuales etiquetas eran HTML. También sugerian poner la definición xmlns:html en la etiqueta raiz, lo cual es lógico porque al especificar con :html cuales son debería reconocerlas, pero a mi no me funcionó. Así que hay que declarar xmlns:html y luego todas las etiquetas van de la forma html:nombre_de_etiqueta. Esto es un problema porque hay que usar expresiones regulares para meter el html: en todas las etiquetas, y para colmo cuando se usa xmlhttprequest hay que sacarlas en el Javascript antes de mostrarlas porque las estás metiendo despues de la conversión XSL. Para ello escribí una función que parsea el XML y lo transforma en HTML:

function xml2html(xml)
{
	var i=0;
	var j=0;
	var html= "";
	var tagName = "";
	
	for (i=0;i<xml.childNodes.length;i++)
	{
		/* If it's not a text node then we need to continue searching within it */
		if (xml.childNodes[i].nodeType != 3)
		{
			/* Test for single tag elements */
			if (xml.childNodes[i].childNodes.length!=0)
			{
				/* Converts html:b into <b></b> with the stuff inside */
				tagName = xml.childNodes[i].nodeName.split(':')[1];
				fullTag = tagName;
				for(j=0;j<xml.childNodes[i].attributes.length;j++)
				{
					fullTag = fullTag + ' '+xml.childNodes[i].attributes[j].name+'='+xml.childNodes[i].attributes[j].value;
				}
				html = html + '<'+fullTag+'>' + xml2html(xml.childNodes[i]) + '</'+tagName+'>';
			} else {
				/* This is for single tag elements i.e.: <br /> and <img /> */
				fullTag = xml.childNodes[i].nodeName.split(':')[1];
				for(j=0;j<xml.childNodes[i].attributes.length;j++)
				{
					fullTag = fullTag + ' '+xml.childNodes[i].attributes[j].name+'='+xml.childNodes[i].attributes[j].value;
				}
				html = html + '<'+fullTag+' />';
			}
		} else {
			/* If it is a text node, just get the data and append to previous html */
			html = html + xml.childNodes[i].data;
		}
	}
	return html;
}

Un último detalle: para usarlo en una conversión XSL hay que usar la etiqueta <xsl:copy-of select="event//description" /> y no <xsl:value-of select="event//description" />. No me acuerdo que cosa mágica hace copy que no hace value pero sé que sino no funciona.

 

4 Responses to HTML, XML y XSL: Guia de convivencia

  1. Federico says:

    ¿Encerrar el HTML en una sección CDATA no era una opción?

  2. Hermann says:

    Si, esa opcion me la olvide, pero tampoco me funciono, Es mas, fue lo primero que probe, porque asi se hace en el feed RSS. El problema viene por culpa de la transformacion XSL, asi que lo que funciona para el feed RSS no tiene porque funcionar en la transformacion.

  3. Tuxiradical says:

    :D ¡Ya era hora! Hace meses que lo prometiste :P

    PD: ¿popular?

  4. Federico says:

    Curioso lo que decis. CDATA es parte de XML (está más alla de RSS). Mala suerte entonces.
    Notece que igualmente toco bastante de oido. :)

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Set your Twitter account name in your settings to use the TwitterBar Section.