CERCA SITEMAP FEED RSS 1280
Ultimo aggiornamento: 30 Agosto 2009

Il linguaggio XSLT

XSLT (Extensible Stylesheet Language for Trasformation) è un linguaggio xml-based proposto dal World Wide Web Consortium per applicare delle trasformazioni a documenti xml.
Il linguaggio XSLT può essere ad esempio utilizzato per definire una serie di regole che interpretate dal processore XSLT permettono di trasformare il contenuto di un documento xml in un altro formato quale potrebbe essere un file html, un pdf, una jpeg o altro ancora.
Il W3C definisce due standard per i fogli di stile: CSS (Cascading Style Sheets) e XSLT (Extensible Stylesheet Language for Trasformation).
I CSS sono generalmente associati alle pagine web per definire quelle che sono le caratteristiche della pagina dal punto di vista della presentazione dei contenuti: utilizzando i CSS ad esempio è possibile definire tutte le proprietà degli elementi di markup come colori, bordi, posizionamento, font utilizzati e molto altro ancora.
I CSS tuttavia convivono con alcuni limiti di non poco conto:
  • non possono cambiare l’ordine con i quali gli elementi di markup appariranno all’interno del documento
  • non possono effettuare operazioni logiche ed in generale computazionali
  • non possono operare su più documenti contemporaneamente
XSLT nasce per colmare tali lacune permettendo trasformazioni più potenti e flessibili dei documenti.
XSLT è fortemente basato sulla corrispondenza di pattern: una regola di trasformazione definita all’interno di un foglio XSLT individua mediante espressioni XPath quelli che sono i nodi del documento interessati e quindi ne applica la trasformazione.
In XSLT non esistono cicli for o do-while ma esistono iterazioni e ricorsioni: l’iterazione permette di comunicare al processore XSLT regole del tipo "riconosci tutti gli elementi simili a questo pattern ed applica questa regola per ciascuno di tali elementi", la ricorsione invece consiste nell’applicare una regola di trasformazione che al suo interno richiama se stessa o altre regole di trasformazione.
Fasi della trasformazione
La trasformazione di un documento xml si sviluppa in 5 fasi:
  • il processore XSLT legge l’insieme delle regole di trasformazione definite all’interno del foglio XSLT.
  • viene generata una struttura di dati ad albero contenente l’insieme delle regole di trasformazione
  • viene letto il contenuto del documento xml sorgente
  • viene generata la struttura di dati ad albero rappresentativa del documento xml sorgente
  • ha inizio la trasformazione vera e propria.

La struttura di un foglio XSLT

Supponiamo di avere il seguente documento xml:
<?xml version="1.0"?>
<messaggio>
   Contenuto del messaggio
</messaggio>
e di volerlo trasformare, mediante un foglio XSLT, in una pagina visibile su un comunissimo browser web, quale potrebbe essere la seguente:
<html>
   <body>
      Contenuto del messaggio
   </body>
</html>
Una delle possibili soluzioni protebbe essere rappresentata dal seguente documento XSLT:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:output method="html"/>

<xsl:template match="/">
   <xsl:apply-templates select="messaggio"/>
</xsl:template>

<xsl:template match="messaggio">
   <html>
      <body>
         <xsl:value-of select="."/>
      </body>
   </html>
</xsl:template>

</xsl:stylesheet>
Come notiamo ogni foglio XSLT inizia con un elemento <xsl:stylesheet> che definisce la versione di XSLT ed il namespace che si stanno utilizzando.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
Quindi è presente un elemento <xsl:output> che specifica il metodo di output: valori possibili sono xml, html, text
<xsl:output method="html"/>
ed uno o più elementi <xsl:template> che definiscono le regole di trasformazione applicabili al documento.
L’elemento <xsl:template> è certamente il più importante fra gli elementi definiti dalle specifiche XSLT in quanto definisce una regola (ovvero un modello) di trasformazione.
L’insieme dei nodi interessati dalla trasformazione rappresentata dall’elemento <xsl:template> è definito dall’espressione XPath indicata come valore dell’attributo match.

Tornando al nostro esempio il template
<xsl:template match="/">
   <xsl:apply-templates select="messaggio"/>
</xsl:template>
definisce una regola di trasformazione per l’elemento root del documento (in XPath infatti il carattere /, definito nell’esempio come valore dell’attributo match, sta ad indicare la radice del documento).
XSLT fa un abbondante uso della ricorsione: un elemento <xsl:template> potrebbe infatti richiamare altri template e questi potrebbero a loro volta richiamare altri elementi template... e così via.
Nel nostro esempio l’elemento <xsl:apply-template select="messaggio"> comunica al processore XSLT di applicare le regole di trasformazione definite all’interno del foglio XSLT agli elementi "messaggio" figli dell’elemento radice.
Il processore quindi esamina l’insieme delle regole di trasformazione e scopre che ne esiste una che "matcha" con l’elemento messaggio e quindi la applica:
<xsl:template match="messaggio">
   <html>
      <body>
         <xsl:value-of select="."/>
      </body>
   </html>
</xsl:template>
Come vediamo nel corpo del nuovo template sono contenuti alcuni tag html che verranno direttamente portati in output ed un elemento <xsl:value-of> che tratteremo in seguito ed il cui compito nell’esempio presentato è quello di estrarre il contenuto dell’elemento <messaggio>.

Il risultato di tali trasformazioni è il documento html:
<html>
   <body>
      Contenuto del messaggio
   </body>
</html>
E’ opportuno precisare che se all’interno di un foglio XSLT sono definite più regole applicabili ad un elemento il processore applica quella più specifica ovvero quella che ha un matching maggiormente definito.
Se ad esempio, nel nostro documento XSLT fosse stata definita la regola:
<xsl:template match="/*">

...

</xsl:template>
applicabile a tutti gli elementi figli della radice (questo è infatti il significato dell’espressione XPath "/*") il processore avrebbe continuato ad applicare il template <xsl:template match="messaggio"> per gli elementi messaggio (perchè maggiormente definito) ed avrebbe utilizzato la nuova regola per altri eventuali elementi presenti nel documento e figli della radice.

Leggere il contenuto di un elemento

Per accedere al contenuto di un elemento, indipendentemente dal tipo, XSLT mette a disposizione l’elemento <xsl:value-of> il cui attributo select permette di specificare l’espressione XPath che identifica l’elemento al quale vogliamo avere accesso.

Così, supponendo di avere il seguente documento xml
<?xml version="1.0"?>
<cartella>
   <documento>
      <messaggio tipo="testo" >
         Contenuto del messaggio
      </messaggio>
   <documento>
</cartella>
il template:
<xsl:template match="/">
   <xsl:value-of select="cartella/documento/messaggio" />
</xsl:template>
ci consentirebbe di portare in output il contenuto dell’elemento messaggio, ovvero la stringa "Contenuto del messaggio" mentre il template
<xsl:template match="/">
   <xsl:value-of select="cartella/documento/messaggio/@tipo" />
</xsl:template>
ci consentirebbe di portare in output il contenuto dell’attributo tipo dell’elemento messaggio, ovvero la stringa "testo".

Logica ed iterazione

Per compiere trasformazioni più o meno complesse mediante XSLT dobbiamo ricorrere all’utilizzo di strutture logiche.

L'elemento <xsl:if>

L’elemento <xsl:if> implementa il costrutto if comune a moltissimi linguaggi di programmazione.
Tale elemento accetta un solo attributo test: se il valore di tale attributo viene valutato true allora gli elementi compresi fra i tag di apertura <xsl:if> e chiusura </xsl:if> vengono elaborati, viceversa vengono trascurati.
L’esempio seguente stampa in output la stringa "Maggiorenne" in grassetto solo se l’elemento figlio eta del nodo corrente ha un valore maggiore o uguale a 18.
<xsl:if test="eta >= 18">
   <b>
      Maggiorenne
   </b>
</xsl:if>
Da notare l’utilizzo dell’entità &gt; al posto del segno > (un foglio XSLT è un documento xml pertanto deve essere se non valido almeno ben formato)

Il processore XSLT trasforma il contenuto dell’attributo test in un tipo booleano secondo le seguenti regole:
  • se il contenuto dell’attributo è di tipo numerico allora se è negativo, pari a zero o NaN (Not a Number) restituisce false, in tutti gli altri casi restituisce true.
  • se il contenuto di un attributo è un node-set allora restituisce false se il node-set è vuoto, true altrimenti.
  • se il contenuto è una stringa, restituisce false se la lunghezza è pari a zero, true altrimenti.

L'elemento <xsl-choose>

L’elemento <xsl:choose> è equivalente ai costrutti case e switch dei principali linguaggi di programmazione (ma in XSLT serve ad implementare anche il costrutto if ... then ... else).
<xsl:choose> contiene uno o più elementi <xsl:when> (il cui funzionamento è tale e quale agli elementi <xsl:if>) ed un opzionale elemento <xsl:otherwise>.

Il processore XSLT controlla uno per uno gli elementi <xsl:when> verificando se l’attributo test restituisce true o false:
  • se restituisce true allora gli elementi compresi fra i tag di apertura <xsl:when> e chiusura </xsl:when> vengono elaborati.
  • se restituisce false allora il processore passa alla valutazione dell’elemento <xsl:when> successivo.
Se nessun elemento <xsl:when> viene valutato positivamente il processore esegue, se è presente, il contenuto dell’elemento <xsl:otherwise>
<xsl:choose>
   <xsl:when test="eta >= 18">
      <b>
         Maggiorenne
      </b>
   </xsl:when>
   <xsl:otherwise>
      <b>
         Minorenne
      </b>
   </xsl:otherwise>
</xsl:choose>

L'elemento <xsl:for-each>

L’elemento <xsl:for-each> permette di definire un’iterazione ovvero di selezionare un insieme di nodi (mediante un’espressione XPath indicata come valore dell’attributo select) e fare qualcosa per ciascuno di essi.

Supponendo di avere un documento xml del tipo:
<?xml version="1.0"?>
<gruppo>
   <persona nome="Marco">
      <eta>21</eta>
   </persona>
   <persona nome="Lucia">
      <eta>45</eta>
   </persona>
   <persona nome="Daniele">
      <eta>8</eta>
   </persona>
</gruppo>
per stampare l’età di tutte le persone è possibile scrivere (se stiamo elaborando il nodo "gruppo"):
<xsl:for-each select="persona">
   <xsl:value-of select="eta" />
</xsl:for-each>

Invocare template attraverso il nome

Negli esempi riportati finora l’invocazione ricorsiva di template si è avvalsa dell’utilizzo dell’elemento <xsl:apply_templates> il quale permette di applicare le regole di trasformazione definite all’interno del documento XSLT agli elementi individuati dall’espressione XPath indicata come valore dell’attributo select.
<xsl:template match="/">
   <xsl:apply-templates select="messaggio"/>
</xsl:template>
Quanto l’attributo select non viene indicato <xsl:apply-templates> agisce di default sui figli del nodo correntemente selezionato.
<xsl:template match="/">
   <xsl:apply-templates />
</xsl:template>
Per invocare un template in base al nome occorre associare un nome al template ed utilizzare l’elemento <xsl:call-template> specificando attraverso l’attributo name il nome del template che vogliamo invocare:
<xsl:template match="/">
   <xsl:call-template name="intestazione" />
</xsl:template

<xsl:template name="intestazione">

...

</xsl:template>
L’elemento <xsl:template> possiede fra gli altri un attributo mode che permette di processare uno stesso insieme di nodi in maniera differente.
Se ad esempio volessi utilizzare uno stesso template per processare diversamente un elemento <span> a seconda che si trovi all’interno di un elemento <p> o di un elemento <div> potrei definire i seguenti elementi:
<xsl:template match="span" mode="sotto-p">

... codice da eseguire se lo span si trova dentro un elemento p

</xsl:template>
<xsl:template match="span" mode="sotto-div">

... codice da eseguire se lo span si trova dentro un elemento div

</xsl:template>
e poi invocare il template utilizzando l’elemento <xsl:apply-templates> e specificando il mode di volta in volta:
<xsl:apply-templates mode="sotto-p" select="span" />

<xsl:apply-templates mode="sotto-div" select="span" />
Per definire dei parametri all’interno di un template è possibile utilizzare l’elemento <xsl:param> specificando mediante l’attributo name il nome del parametro stesso
<xsl:param name="altezza" />
Opzionalmente è possibile specificare un valore di default per i parametri:
  • mediante l’attributo select
    <xsl:param name="altezza" select="150"/>
    
  • specificandone il valore all’interno del corpo dell’elemento <xsl:param>
    <xsl:param name="altezza">
       <xsl:text>150</xsl:text>
    </xsl:param>
    
    Per invocare un template passando dei parametri è possibile utilizzare l’elemento <xsl:with-param> nel corpo dell’elemento <xsl:call-template> o <xsl:apply-templates> indicando il nome del parametro ed il valore:
  • mediante l’attributo select
    <xsl:param name="altezza">
       <xsl:text>150</xsl:text>
    </xsl:param>
    
Per invocare un template passando dei parametri è possibile utilizzare l’elemento <xsl:with-param> nel corpo dell’elemento <xsl:call-template> o <xsl:apply-templates> indicando il nome del parametro ed il valore:
  • mediante l’attributo select
    <xsl:call-template name="stampa-altezza">
       <xsl:with-param name="altezza" select="150" />
    </xsl:call-template>
    
  • nel corpo dell’elemento <xsl:with-param> stesso
    <xsl:call-template name="stampa-altezza">
       <xsl:with-param name="altezza">
          <xsl:text>150</xsl:text>
       </xsl:with-param>
    </xsl:call-template>
    
XSLT permette di definire anche dei parametri globali: questi vanno indicati mediante l’elemento <xsl:param> all’interno dell’elemento <xsl:stylesheet>

Le variabili

La definizione di variabili avviene mediante l’elemento <xsl:variable> il cui attributo name permette di specificarne il nome, e può assumere tre distinte forme:
  • creazione di una variabile il cui valore è una stringa vuota
    <xsl:variable name="miavariabile" />
    
  • creazione di una variabile avente valore definito dall’attributo select
    <xsl:variable name="miavariabile" select="150" />
    
  • creazione mediante inclusione di contenuto nel corpo dell’elemento <xsl:variabile>
    <xsl:variable name="miavariabile">
       <xsl:text>150</xsl:text>
    </xsl:variable>
    
Quest’ultimo caso è certamente quello più interessante in quanto permette di definire delle variabili il cui valore dipende dall’elaborazione degli elementi contenuti nel suo corpo.
Una variabile è visibile all’interno dell’elemento entro il quale viene definita: per creare variabili con visibilità globale è possibile definirle nel corpo dell’elemento <xsl:stylesheet>
L’accesso ad una variabile avviene anteponendo il carattere $ al nome della variabile

Creazione di elementi e attributi

Abbiamo già visto che il corpo di un elemento <xsl:template> può contenere elementi che se riconosciuti come non appartenenti alle specifiche XSLT vengono ricopiati in output: questo è soltanto uno dei modi con i quali è possibile creare un elemento.
Un modo alternativo consiste nell’utilizzare il tag <xsl:element> i cui attributi name e namespaces(quest’ultimo opzionale) permettono di specificare nome e namespace di appartenenza dell’elemento stesso.

Così le espressioni
<xsl:template match="/">
   <html>
      <body>
         Corpo della pagina
      </body>
   </html>
</xsl:template>
e
<xsl:template match="/">
   <xsl:element name="html" >
      <xsl:element name="body">
         <xsl:text>Corpo della pagina</xsl:text>
      </xsl:element>
   </xsl:element>
</xsl:template>
possono considerarsi del tutto equivalenti.

La creazione degli attributi di un elemento può invece avvenire in due distinti modi:
  • utilizzando l’elemento <xsl:attribute> ed indicando negli attributi name e namespace (opzionale) il nome e il namespace di appartenenza dell’attributo
    <xsl:element name="persona">
       <xsl:attribute name="eta">
          <xsl:value-of select="eta" />
       </xsl:attribute>
    </xsl:element>
    
  • utilizzando una forma abbreviata che prevede l’utilizzo delle parentesi graffe per recuperare mediante espressione XPath il valore dell’attributo associato all’elemento
    <persona eta="{eta}" />
    
Oltre a xsl:elemento e xsl:attribute, XSLT mette a disposizione elementi per la creazione di:
  • commenti: mediante <xsl:comment> specificando fra i tag di apertura e chisura il testo del commento
    <xsl:comment>
       Questo è un commento
    </xsl:comment>
    
  • processing instruction: si utilizza <xsl:processing-instruction> specificando mediante l’attributo name il nome ed inserendone il contenuto fra i tag di apertura e chiusura
    <xsl:processing-instruction name="miaprocessinginstruction">
       Contenuto della processing instruction
    </xsl:processing-instruction>
    
  • testo: si usa <xsl:text> specificando nel corpo il contenuto della sezione CDATA
    <xsl:text> testo </xsl:text>
    
Particolarmente interessante è l’elemento <xsl:message> che può essere utilizzato per gestire le situazioni di errore che si verificano nel corso della trasformazione del documento xml mediante il foglio XSLT.
<xsl:message> accetta un attributo terminate che può assumere valori yes o no e che interrompe o meno la trasformazione del documento una volta che il contenuto del messaggio viene stampato in output.
La seguente espressione ad esempio arresta la trasformazione del documento xml quando incontra un valore di età negativo e ne stampa in output l’errore.
<xsl:if test="eta < 0">
   <xsl:message terminate="yes"> Errore: età negativa</xsl:message>
</xsl:if>;

Ordinamento degli elementi

L’elemento <xsl:sort> può essere utilizzato nel corpo degli elementi (e prima di qualsiasi altro elemento) <xsl:for-each> ed <xsl:apply-templates> per ordinare gli elementi del documento xml prima che questi subiscano la trasformazione.

L’elemento xsl:sort accetta 5 attributi:
  • select individua mediante espressione XPath gli elementi in base ai quali effettuare l’ordinamento
  • lang definisce il linguaggio utilizzato per l’ordinamento
  • data-type permette di precisare il tipo degli elementi rispetto ai quali stiamo effettuando l’ordinamento
  • order viene utilizzato per indicare ordinamento crescente (ascending) o discendente (descending)
  • case-order permette di specificare se nel corso dell’ordinamento bisogna dare precedenza ai caratteri minuscoli (lower-first) o maiuscoli (upper-first).
Supponendo di avere il seguente documento xml:
<?xml version="1.0"?>
<gruppo>
   <persona nome="Marco">
      <eta>21</eta>
   </persona>
   <persona nome="Lucia">
      <eta>45</eta>
   </persona>
   <persona nome="Daniele">
      <eta>8</eta>
   </persona>
</gruppo>
la seguente espressione stampa i nomi delle persone da quella che ha l’età più piccola a quella che ha l’età più grande.
<xsl:template match="/gruppo">
   <xsl:for-each select="persona">
      <xsl:sort select="eta" order="ascending" />
      <xsl:value-of select="@nome">
   </xsl:for-each>
</xsl:template>
Se si vuole effettuare l’ordinamento in base ai più attributi è possibile utilizzare più elementi xsl:sort posti uno sotto l’altro.

Includere ed importare fogli XSLT

XSLT permette mediante gli elementi <xsl:import> ed <xsl:include> di importare ed includere fogli XSLT dentro altri: la principale differenza fra l’importazione e l’inclusione sta nella priorità con la quale i documenti XSLT esterni vengono trattati.
Nel caso dell’inclusione le regole definite nel documento incluso hanno infatti la stessa priorità delle regole definite nel documento XSLT principale, nel caso dell’importazione invece le regole definite nel documento principale hanno una priorità maggiore.
Per includere un foglio di stile esterno è sufficiente inserire l’elemento <xsl:include> nel corpo dell’elemento <xsl:stylesheet>.

Così supponendo di avere un foglio XSLT esterno foglio-esterno.xsl per includerlo nel foglio principale si dovrà scrivere:
<xsl:include href="foglio-esterno.xsl" />
Per importare un foglio di stile esterno invece si dovrà dapprima inserire l’elemento <xsl:import> nel corpo dell’elemento <xsl:stylesheet> e quindi utilizzare l’elemento <xsl:apply-imports /> per applicare le regole del foglio di stile importato.
<xsl:import href="foglio-esterno.xsl" />

...

<xsl:template match="/">
   <xsl:apply-imports />
</xsl:template>

...

Lavorare con più documenti XML

All’inizio di questo articolo è stato fatto un accenno a quella che è la possibilità di XSLT di accedere ai nodi di un documento xml esterno a quello che si sta elaborando.
Tale possibilità è concessa dall’utilizzo della funzione document().
Supponendo di voler recuperare il valore di un attributo di un elemento contenuto in un documento xml esterno potremmo scrivere:
<xsl:value-of select="document(’documento-esterno.xml’)/cartella/documento/@tipo" /> 
La funzione document in questo caso agisce sul documento xml esterno e recupera il valore dell’attributo tipo dell’elemento documento.