Nella lezione precedente si è giunti ad una situazione in cui, terminato di trattare con l'usuale approccio procedurale il codice XML che organizza i messaggi all'interno di un’ipotetica applicazione, risultava difficile elaborare il contenuto stesso dei messaggi, di formato XHTML, per il livello di nidificazione virtualmente illimitato che quest’ultimo consente.
Contrariamente alla stretta gabbia XML che lo circonda, il linguaggio debolmente strutturato in essa contenuto costringe, infatti, ad adottare un punto di vista differente, che non faccia affidamento su di una posizione precisa degli elementi da esaminare e che debba pertanto accontentarsi di “attenderli al varco".
Introduciamo quindi uno degli elementi più significativi del linguaggio di trasformazione, sebbene non produca di per se alcun output durante l'esecuzione: l'elemento apply-templates. Il suo scopo è semplicemente quello di selezionare un insieme di nodi muovendo dal contesto attuale, ovvero il punto dell'alberatura di un sorgente XML nel quale ci si trova in una data fase dell'esecuzione.
Se non viene specificato alcun valore per l'attributo select, lo stesso incontrato nell’elemento value-of, vengono selezionati tutti i nodi del contesto attuale; se invece specifichiamo l’elemento content come selettore XPath otterremo una rapida soluzione per completare l'esempio dello scorso articolo.
Una volta selezionati gli elementi di interesse è necessario tuttavia disporre di un template che, in corrispondenza di essi, produca l'output richiesto: nell'esempio tutto ciò che volevamo realizzare è una copiatura dei contenuti senza modifica alcuna e per questo esiste l'elemento copy. Il codice in grado di eseguire tutto questo è contenuto in poche righe del file esempio3.xsl :
...
<xsl:value-of select="from" />
</td>
<td>
<xsl:apply-templates select="content" />
</td>
</tr>
</xsl:for-each>
</xsl:template>
<xsl:template match="*|@*|text()">
<xsl:copy>
<xsl:apply-templates select="*|@*|text()"/>
</xsl:copy>
</xsl:template>
Si potrebbe obiettare che, inserendo le tre righe che eseguono l'effettiva copiatura al posto dell'elemento apply-templates, si possa ottenere lo stesso risultato in modo ancora più conciso.
Ciò è vero nel caso specifico dell'esempio ma non in assoluto: può infatti accadere molte volte nell’esecuzione del foglio di stile di aver bisogno di questa stessa funzione di copiatura, e questo richiederebbe di duplicare ogni volta le tre righe che la eseguono.
Il modello d’esecuzione di XSLT sembra quindi consigliare una netta divisione tra la fase di raccolta dei contenuti (l’invocazione di apply-templates) e quella di produzione dell'output (l’attivazione di un template opportuno). Lasciando libero il template di catturare i nodi disponibili è anche possibile accorgersi più facilmente di eventuali dati, da trattare in modo particolare, sfuggiti in qualche modo all'esecuzione: li ritroveremmo infatti nell'output finale senza modifiche.
Il template proposto accoglie ogni tipo d’elemento, (*), ogni possibile attributo (@*) e ogni nodo di testo, ponendoli in alternativa all'interno dell'espressione XPath con la barra verticale (|). L'utilizzo tipico vede invece ben specificate le tipologie di elementi che sono in grado di attivare un template. La coppia di attributi select e match rappresentano in sintesi il punto di contatto tra le due fasi dell'elaborazione prima citate.
Guardando il file HTML prodotto, noteremo però la presenza dell’elemento content, che avremmo preferito perdere lungo la via, dal momento che l’attributo select="content" seleziona anche quest’ultimo oltre che il proprio contenuto. D’altra parte l’espressione XPath content/* restituirebbe SOLO gli elementi di XHTML e il loro contenuto, nell’esempio “<b>appuntamento di stasera</b>", mentre tutto il resto andrebbe perso.
L’espressione content/*|content/text() risolve invece il problema, consentendo anche al testo semplice di venire copiato nell’output. Esiste anche una soluzione più elegante, che consiste nell’inserire un template vuoto, ad eccezione dell’elemento apply-templates, che elimini i nodi che non hanno ragione di essere copiati:
<xsl:template match="content">
<xsl:apply-templates />
</xsl:template>
Questa è la pagina prodotta. Per escludere invece dall’output gli elementi di tipo content e tutto il loro contenuto, sarà invece sufficiente specificare una template vuoto che elimini il primo dal flusso di esecuzione:
<xsl:template match="content" />