tutorial - xslt editor
¿En qué orden se ejecutan las plantillas en un documento XSLT y coinciden en el código fuente XML o en el resultado almacenado? (4)
En su primer ejemplo, se ejecuta la Plantilla n. ° 1 porque cuando comienza a procesar la entrada xml comienza en la raíz y esa es la única plantilla en su hoja de estilo que coincide con el elemento raíz. Incluso si fuera el 2º en la hoja de estilo, seguiría ejecutándose primero.
En este ejemplo, la plantilla 2 no se ejecutará ya que usted ya ha procesado el elemento raíz usando la plantilla 1 y no hay más elementos para procesar después de la raíz. Si desea procesar otros elementos utilizando plantillas adicionales, debe cambiarlo a.
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
Esto le permite definir una plantilla para cada elemento que le interesa y procesar el xml de una manera más lógica, en lugar de hacerlo procesalmente.
También tenga en cuenta que este ejemplo no dará como resultado nada, ya que en el contexto actual (la raíz) no hay elemento firstName, solo un elemento persona, por lo que debería ser:
<xsl:template match="/">
<xsl:value-of select="person/firstName"/> <xsl:value-of select="person/lastName"/>
</xsl:template>
Me resulta más fácil pensar que está pasando por el xml, comenzando desde la raíz y buscando la plantilla que coincida con ese elemento, luego siga esas instrucciones para generar la salida. El XSLT transforma el documento de entrada en la salida para que el documento de salida esté vacío al comienzo de la transformación. La salida no se usa como parte de la transformación, solo es la salida de la misma.
En su segundo ejemplo, la Plantilla # 2 no se ejecutará porque la plantilla se ejecuta contra la entrada xml, no como la salida.
Aquí hay algo que siempre me ha desconcertado acerca de XSLT:
- ¿En qué orden se ejecutan las plantillas, y
- Cuando se ejecutan, ¿coinciden en (a) el origen XML original, o (b) la salida actual del XSLT a ese punto?
Ejemplo:
<person>
<firstName>Deane</firstName>
<lastName>Barker</lastName>
</person>
Aquí hay un fragmento de XSLT:
<!-- Template #1 -->
<xsl:template match="/">
<xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
</xsl:template>
<!-- Template #2 -->
<xsl:template match="/person/firstName">
First Name: <xsl:value-of select="firstName"/>
</xsl:template>
Dos preguntas sobre esto:
- Estoy asumiendo que la Plantilla # 1 se ejecutará primero. No sé por qué asumo esto, ¿es solo porque aparece primero en el documento?
- ¿Se ejecutará la Plantilla # 2? Coincide con un nodo en el código fuente XML, pero para cuando lleguemos a esta plantilla (suponiendo que se ejecute en segundo lugar), el nodo "firstName" no estará en el árbol de salida.
Entonces, ¿las plantillas "posteriores" están en deuda con lo que ha ocurrido en las plantillas "anteriores", o operan en el documento fuente, ajenos a lo que se ha transformado "antes" de ellas? (Todas esas palabras están entre comillas, porque me resulta difícil analizar los problemas basados en el tiempo cuando, en primer lugar, tengo muy poca idea de cómo se determina el orden de las plantillas ...)
En el ejemplo anterior, tenemos una plantilla que coincide en el nodo raíz ("/") que, cuando se termina de ejecutar, esencialmente ha eliminado todos los nodos de la salida. Siendo este el caso, ¿esto evitaría que se ejecuten todas las demás plantillas, ya que no hay nada que coincida después de que se complete la primera plantilla?
Hasta este punto, me han preocupado las plantillas posteriores que no se ejecutan porque los nodos que han operado no aparecen en el resultado, pero ¿qué pasa con el inverso? ¿Puede una plantilla "anterior" crear un nodo con el que una plantilla "posterior" puede hacer algo?
En el mismo XML que el anterior, considere este XSL:
<!-- Template #1 -->
<xsl:template match="/">
<fullName>
<xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
</fullName>
</xsl:template>
<!-- Template #2 -->
<xsl:template match="//fullName">
Full Name: <xsl:value-of select="."/>
</xsl:template>
La plantilla # 1 crea un nuevo nodo llamado "fullName". La plantilla # 2 coincide en ese mismo nodo. ¿Se ejecutará la Plantilla # 2 porque el nodo "nombre completo" existe en el resultado cuando llegamos a la Plantilla # 2?
Me doy cuenta de que soy profundamente ignorante sobre el "zen" de XSLT. Hasta la fecha, mis hojas de estilo han consistido en una plantilla que coincide con el nodo raíz, luego son completamente procedurales a partir de ahí. Estoy cansado de hacer esto. Preferiría realmente entender XSLT correctamente, de ahí mi pregunta.
La respuesta de Evan es básicamente buena.
Sin embargo, una cosa que parece faltar es la capacidad de "invocar" fragmentos de código sin hacer ninguna coincidencia. Esto permitiría, al menos en opinión de algunas personas, una estructuración mucho mejor.
He hecho un pequeño ejemplo en un intento de mostrar lo que quiero decir.
<xsl:template match="/" name="dotable">
<!-- Surely the common html part could be placed somewhere else -->
<!-- the head and the opening body -->
<html>
<head><title>Salary table details</title></head>
<body>
<!-- Comments are better than nothing -->
<!-- but that part should really have been somewhere else ... -->
<!-- Now do what we really want here ... this really is making the table! -->
<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
<tr>
<td><xsl:value-of select="name" /></td>
<td><xsl:value-of select="firstname" /></td>
<td><xsl:value-of select="age" /></td>
<td><xsl:value-of select="salary" /></td>
</tr>
</xsl:for-each>
</table>
<!-- Now close out the html -->
</body>
</html>
<!-- this should also really be somewhere else -->
<!-- This approach works, but leads to horribly monolithic code -->
<!-- Further - it leads to templates including code which is strictly -->
<!-- not relevant to them. I''ve not found a way round this yet -->
</xsl:template>
Sin embargo, después de juguetear un poco, y al principio hacer uso de la sugerencia de que si hay dos plantillas coincidentes, se seleccionará la última en el código y luego reestructurar mi código (no todo se muestra aquí), logré esto que parece para trabajar, y con suerte genera el código correcto, así como también muestra los datos deseados -
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- <?xml version="1.0"?>-->
<xsl:template name="dohtml">
<html>
<xsl:call-template name="dohead" />
<xsl:call-template name="dobody" />
</html>
</xsl:template>
<xsl:template name="dohead">
<head>
<title>Salary details</title>
</head>
</xsl:template>
<xsl:template name="dobody">
<body>
<xsl:call-template name="dotable" />
</body>
</xsl:template>
<xsl:template match="/entries" name="dotable">
<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
<tr>
<td><xsl:value-of select="name" /></td>
<td><xsl:value-of select="firstname" /></td>
<td><xsl:value-of select="age" /></td>
<td><xsl:value-of select="salary" /></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template match="/" name="main">
<xsl:call-template name="dohtml" />
</xsl:template>
[Desplace el código arriba hacia arriba si no puede verlo todo]
La forma en que esto funciona es que la plantilla principal siempre coincide: coincide en /
Esto tiene los trozos de código - plantillas - que se llaman.
Esto significa que no es posible hacer coincidir otra plantilla en / pero es posible hacer coincidir explícitamente en un nodo nombrado, que en este caso es el nodo de nivel más alto en las entradas llamadas xml.
Una pequeña modificación al código produjo el ejemplo dado anteriormente.
Las plantillas siempre coinciden en el código fuente XML. Por lo tanto, el orden en realidad no importa, a menos que 2 o más plantillas coincidan con el mismo nodo (s). En ese caso, de forma un tanto intuitiva, se desencadena la regla con la última plantilla coincidente.
Me encanta tu pregunta Eres muy elocuente sobre lo que aún no entiendes. Solo necesitas algo para unir las cosas. Mi recomendación es que lea "Cómo funciona XSLT" , un capítulo que escribí para abordar exactamente las preguntas que hace. Me encantaría saber si une las cosas para ti.
Menos formalmente, intentaré responder cada una de sus preguntas.
- ¿En qué orden se ejecutan las plantillas, y
- Cuando se ejecutan, ¿coinciden en (a) el origen XML original, o (b) la salida actual del XSLT a ese punto?
En cualquier punto dado del procesamiento XSLT, hay, en cierto sentido, dos contextos, que identifica como (a) y (b): dónde se encuentra en el árbol fuente y dónde se encuentra en el árbol de resultados . El lugar donde se encuentra en el árbol fuente se llama nodo actual . Puede cambiar y saltar alrededor del árbol fuente, ya que eliges conjuntos arbitrarios de nodos para procesar usando XPath. Sin embargo, conceptualmente, nunca "saltas" el árbol de resultados de la misma manera. El procesador XSLT lo construye de forma ordenada; primero crea el nodo raíz del árbol de resultados; luego agrega niños, construyendo el resultado en orden de documentos (primero en profundidad). [Tu publicación me motiva a volver a buscar mi visualización de software para experimentos XSLT ...]
El orden de las reglas de plantilla en una hoja de estilo nunca importa. No se puede decir, simplemente mirando la hoja de estilos, en qué orden se crearán instancias de las reglas de la plantilla, cuántas veces se creará una instancia de una regla, o incluso si se creará alguna instancia. ( match="/"
es una excepción, siempre se puede saber que se activará).
Estoy asumiendo que la Plantilla # 1 se ejecutará primero. No sé por qué asumo esto, ¿es solo porque aparece primero en el documento?
Nop. Se llamaría primero incluso si lo coloca en último lugar en el documento. El orden de las reglas de las plantillas nunca importa (excepto bajo una condición de error cuando tiene más de una regla de plantilla con la misma prioridad que coincide con el mismo nodo, incluso entonces, es opcional para el implementador y nunca debe confiar en dicho comportamiento). Se llama primero porque lo primero que siempre sucede cuando ejecuta un procesador XSLT es una llamada virtual a <xsl:apply-templates select="/"/>
. La llamada virtual única construye todo el árbol de resultados. Nada sucede fuera de eso. Puede personalizar o "configurar" el comportamiento de esa instrucción definiendo reglas de plantilla.
¿Se ejecutará la Plantilla # 2? Coincide con un nodo en el código fuente XML, pero para cuando lleguemos a esta plantilla (suponiendo que se ejecute en segundo lugar), el nodo "firstName" no estará en el árbol de salida.
La plantilla n. ° 2 (ni ninguna otra regla de plantilla) nunca se activará a menos que tenga una llamada <xsl:apply-templates/>
en algún lugar de la regla match="/"
. Si no tiene ninguno, no se activarán reglas de plantilla que no sean match="/"
. Piénselo de esta manera: para que una regla de plantilla se active, no puede coincidir con un nodo en la entrada. Tiene que coincidir con un nodo que elija para procesar (usando <xsl:apply-templates/>
). Por el contrario, continuará coincidiendo con el nodo tantas veces como elija procesarlo.
¿La plantilla [
match="/"
] anticiparía la ejecución de todas las demás plantillas, ya que no hay nada para que coincida después de que se complete la primera plantilla?
Esa regla prevalece sobre el resto de ninguna parte, incluyendo <xsl:apply-templates/>
. Todavía hay muchos nodos que podrían procesarse en el árbol fuente. Siempre están todos ahí, listos para la recolección; procesa cada una cuantas veces quieras. Pero la única forma de procesarlos usando reglas de plantilla es llamar a <xsl:apply-templates/>
.
Hasta este punto, me han preocupado las plantillas posteriores que no se ejecutan porque los nodos que han operado no aparecen en el resultado, pero ¿qué pasa con el inverso? ¿Puede una plantilla "anterior" crear un nodo con el que una plantilla "posterior" puede hacer algo?
No es que una plantilla "anterior" crea un nuevo nodo para ser procesado; es que una plantilla "anterior" a su vez procesa más nodos del árbol fuente, usando la misma instrucción ( <xsl:apply-templates
). Puede pensar que llama a la misma "función" recursivamente, con diferentes parámetros cada vez (los nodos a procesar según lo determinado por el contexto y el atributo de select
).
Al final, lo que obtienes es una pila estructurada por árbol de llamadas recursivas a la misma "función" ( <xsl:apply-templates>
). Y esta estructura de árbol es isomorfa a tu resultado real. No todos se dan cuenta de esto o lo han pensado de esta manera; eso es porque no tenemos ninguna herramienta de visualización efectiva ... todavía.
La plantilla # 1 crea un nuevo nodo llamado "fullName". La plantilla # 2 coincide en ese mismo nodo. ¿Se ejecutará la Plantilla # 2 porque el nodo "nombre completo" existe en el resultado cuando llegamos a la Plantilla # 2?
Nop. La única forma de hacer una cadena de procesamiento es configurarlo explícitamente de esa manera. Cree una variable, por ejemplo, $tempTree
, que contenga el nuevo elemento <fullName>
y luego procese, como este <xsl:apply-templates select="$tempTree">
. Para hacer esto en XSLT 1.0, debe envolver la referencia variable con una función de extensión (por ej., exsl:node-set()
), pero en XSLT 2.0 funcionará tal como está.
Ya sea que esté procesando nodos desde el árbol de origen original o en un árbol temporal que construya, de cualquier forma debe indicar explícitamente qué nodos desea procesar.
Lo que no hemos cubierto es cómo XSLT obtiene todo su comportamiento implícito. También debe comprender las reglas de plantillas incorporadas . Escribo hojas de estilo todo el tiempo que ni siquiera incluyen una regla explícita para el nodo raíz ( match="/"
). En cambio, confío en la regla incorporada para los nodos raíz (aplicar plantillas a los niños), que es la misma que la regla incorporada para los nodos de elementos. Por lo tanto, puedo ignorar grandes partes de la entrada, dejar que el procesador XSLT la recorra automáticamente, y solo cuando se encuentre con un nodo que me interese, haré algo especial. O podría escribir una sola regla que copie todo de forma recursiva (llamada transformación de identidad), reemplazándola solo cuando sea necesario, para hacer cambios incrementales a la entrada. Después de leer "Cómo funciona XSLT", su próxima tarea es buscar la "transformación de identidad".
Me doy cuenta de que soy profundamente ignorante sobre el "zen" de XSLT. Hasta la fecha, mis hojas de estilo han consistido en una plantilla que coincide con el nodo raíz, luego son completamente procedurales a partir de ahí. Estoy cansado de hacer esto. Preferiría realmente entender XSLT correctamente, de ahí mi pregunta.
Te aplaudo. Ahora es el momento de tomar la "píldora roja": lea "Cómo funciona XSLT"