XML/XSLT e fogli di stile
- Scoprire che cos'è l'eXtensible Stylesheet Language (XSL)
- Conoscere la sintassi XSL
- Imparare a utilizzare i template
Nei capitoli precedenti, abbiamo introdotto le basi dell'utilizzo di un foglio di stile XSL per convertire i documenti XML in HTML. Questo capitolo passerà brevemente in rassegna questi concetti e ne introdurrà molti di nuovi. Qui troverete un punto di riferimento per la creazione di fogli di stile.
Foglio di stile XML
modificaL'eXtensible Stylesheet Language (XSL) fornisce un mezzo per trasformare e formattare il contenuto del documento XML per la visualizzazione. Si compone di due parti, XSL Transformation (XSLT) per trasformare il documento XML, e XSLFO (XSL Formatting Objects) per la formattazione o l'applicazione di stili ai documenti XML. Il linguaggio di trasformazione XSL (XSLT) è utilizzato per trasformare documenti XML da una forma all'altra, tra cui nuovi documenti XML, HTML, XHTML, e documenti di testo. XSL-FO consente di formattare i dati contenuti in un documento XML, per la successiva visualizzazione a video, la stampa o la conversione in formati particolari come documenti PDF.
Con XSLT è possibile riciclare in modo efficace i contenuti, rimodellandoli per l'utilizzo nei nuovi documenti, o trasformarli per adattarli ad ogni tipo di uso. Ad esempio, da un singolo file XML di origine, è possibile estrarre un documento pronto per la stampa, uno per il Web, uno per una pagina di manuale Unix, e un altro per un sistema di Guida in linea. Si può anche scegliere di estrarre solo le parti di un documento scritte in una lingua specifica da una sorgente XML che memorizza testo in molte lingue. Le possibilità sono infinite!
Un foglio di stile XSLT è un documento XML, completo di elementi e attributi. Ha due tipi di elementi, di alto livello (“top-level”) e le istruzioni. Gli elementi di alto livello stanno subito sotto l'elemento principale del foglio di stile. Le istruzioni rappresentano un insieme di comandi di formattazione che stabiliscono come il contenuto di un documento XML sarà trasformato.
Durante il processo di trasformazione, XSLT analizza il documento XML, o “albero di origine”, e lo converte in un albero di nodi, una rappresentazione gerarchica del documento XML, anche noto come albero risultato. Ogni nodo rappresenta una parte del documento XML, ad esempio un elemento, attributo o qualche contenuto di testo. Il foglio di stile XSL contiene predefiniti modelli (“templates”) che contengono le istruzioni su cosa fare con i nodi. XSLT utilizzerà l'attributo match
per mettere in relazione gli elementi nodo di XML con i template, e trasformarli nel documento risultato.
Rivediamo il foglio di stile city.xsl del capitolo 2, ed esaminiamo un po' più in dettaglio:
Allegato 1: foglio di stile XML per l'entità città
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document: city.xsl
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head>
<title>Città</title>
</head>
<body>
<h2>Città</h2>
<xsl:apply-templates select="cities"/>
</body>
</html>
</xsl:template>
<xsl:template match="cities">
<!-- l’elemento for-each può essere utilizzato per scorrere ciascun nodo in un insieme di nodi specificato (in questo caso città) -->
<xsl:for-each select="city">
<xsl:text>Città: </xsl:text>
<xsl:value-of select="cityName"/>
<br/>
<xsl:text>Popolazione: </xsl:text>
<xsl:value-of select="cityPop"/>
<br/>
<xsl:text>Paese: </xsl:text>
<xsl:value-of select="cityCountry"/>
<br/>
<br/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
- Dal momento che un foglio di stile è un documento XML, si comincia con la dichiarazione XML. Ciò include gli pseudo-attributi
encoding
estandalone
. Sono chiamati pseudo per distinguerli dagli attributi degli elementi. L'attributo standalone consente di specificare un DTD esterno - Il tag
<xsl:stylesheet>
stabilisce l'inizio del foglio di stile e identifica il numero di versione e il namespace ufficiale del W3C. Si noti il prefisso convenzionale per lo spazio dei nomi XSLT, xsl. Una volta che un prefisso viene dichiarato, deve essere utilizzato per tutti gli elementi. - Il tag
<xsl:output>
è un elemento opzionale che determina il tipo di output dell' albero risultato. - Se nessun tipo di output fosse stato specificato, in questo caso l'output sarebbe stato comunque HTML, in quanto l'elemento radice è il tag di inizio <html>.
- L'elemento
<xsl:template>
stabilisce l'inizio di un template e contiene le regole da applicare quando è associato a un nodo specificato. L'attributo match viene utilizzato per associare un template con un elemento XML, in questo caso la root (/), ma potrebbe essere un suo ramo, del documento XML di origine. apply-templates
applica una regola di template per l'elemento corrente o i nodi figlio dell'elemento. L'attributo select contiene un “path” (percorso) che stabilisce quale contenuto dell'elemento deve essere elaborato.- L'istruzione
value-of
estrae il valore stringa del figlio del nodo selezionato, in questo caso, il testo del nodo figlio di CityName.
L'elemento template definisce le regole che implementano la trasformazione del documento. Questo può prevedere un qualsiasi numero di elaborazioni, tra cui una semplice conversione di testo, l'aggiunta o la rimozione di elementi XML, o una conversione in formato HTML, quando il modello è stato associato. Il modello, definito nell' attributo match dell'elemento, contiene un breve percorso in formato XPath. Questo è di base il nome dell'elemento radice del documento, nel nostro caso "tourGuide."
Quando si trasforma un documento XML in HTML, il processore si aspetta che gli elementi nel foglio di stile siano ben formati, proprio come nell'XML. Ciò significa che tutti gli elementi devono avere un tag di chiusura. Per esempio, non è raro vedere in una pagina html il tag <p>
solo. Il processore XSLT richiede che un elemento con un tag di inizio deve essere chiuso con un end tag. Per l'elemento <br>
, questo significa utilizzare <br>
</ br>
o <br />
. Anche se nell’output HTML un tag non risulterà chiuso, in ogni caso questo deve avere un end-tag nel foglio di stile. Per esempio, nel foglio di stile, dovrà comparire <img src="picture.jpg"> </ img>
o come elemento vuoto <img src="picture.jpg" />
. L'output HTML invece farà cadere il tag di chiusura e l’elemento apparirà in questo modo: <img src="picture.jpg">
. Come nota a margine, aggiungiamo che il processore riconosce i tag html senza curarsi che sinao in maiusolo o minuscolo: BODY, body, Body sono tutti interpretati allo stesso modo (non è “case-sensitive”).
Output
modificaXSLT può essere utilizzato per trasformare una sorgente XML in molti diversi tipi di documenti. XHTML è XML, se ben formato, e quindi può essere utilizzato sia come sorgente che come risultato. Tuttavia, non è possibile trasformare il codice HTML in XML se prima non viene trasformato in XHTML, in modo che sia conforme allo standard XML 1.0. Ecco una lista di tutte le possibili trasformazioni da un tipo a un alto eseguite da XSLT:
Tabella 1: Trasformazioni Type-To-Type
XML | XHTML | HTML | text | |
XML | X | X | X | X |
XHTML | X | X | X | X |
HTML | ||||
text |
L'elemento output nel foglio di stile determina il tipo di output dell' albero risultato, specificato nell’attributo method
. Questo elemento è facoltativo, ma permette di avere un maggiore controllo sull'output. Se non lo si include nel documento, il tipo di output per impostazione predefinita sarà XML, o HTML se il primo elemento nell'albero risultato è l'elemento <html>.
Da XML a XML
modificaFinora abbiamo visto come trasformare un documento XML in HTML, ora invece ci occuperemo di come trasformare city.xml, usato nel capitolo 2, in un altro file XML, utilizzando host.xsd come schema.
Allegato 2: documento XML per l'entità città
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document: city.xml
-->
<cities xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:noNamespaceSchemaLocation='host.xsd'>
<city>
<cityID>c1</cityID>
<cityName>Atlanta</cityName>
<cityCountry>USA</cityCountry>
<cityPop>4000000</cityPop>
<cityHostYr>1996</cityHostYr>
</city>
<city>
<cityID>c2</cityID>
<cityName>Sydney</cityName>
<cityCountry>Australia</cityCountry>
<cityPop>4000000</cityPop>
<cityHostYr>2000</cityHostYr>
<cityPreviousHost>c1</cityPreviousHost >
</city>
<city>
<cityID>c3</cityID>
<cityName>Athens</cityName>
<cityCountry>Greece</cityCountry>
<cityPop>3500000</cityPop>
<cityHostYr>2004</cityHostYr>
<cityPreviousHost>c2</cityPreviousHost >
</city>
</cities>
Allegato 3: documento XSL per l'entità città che elenca le città per ID
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select="//city[count(cityPreviousHost) = 0]">
<br/><xsl:text>City Name: </xsl:text><xsl:value-of select="cityName"/><br/>
<xsl:text> Rank: </xsl:text><xsl:value-of select="cityID"/><br/>
<xsl:call-template name="output">
<xsl:with-param name="context" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="output">
<xsl:param name="context" select="."/>
<xsl:for-each select="//city[cityPreviousHost = $context/cityID]">
<br/><xsl:text>City Name: </xsl:text> <xsl:value-of select="cityName"/><br/>
<xsl:text> Rank: </xsl:text><xsl:value-of select="cityID"/><br/>
<xsl:call-template name="output">
<xsl:with-param name="context" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Allegato 4: XML schema per l’entità host city
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:element name="cities">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="city" type="cityType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="cityType">
<xsd:sequence>
<xsd:element name="cityID" type="xsd:ID"/>
<xsd:element name="cityName" type="xsd:string"/>
<xsd:element name="cityCountry" type="xsd:string"/>
<xsd:element name="cityPop" type="xsd:integer"/>
<xsd:element name="cityHostYr" type="xsd:integer"/>
<xsd:element name="cityPreviousHost" type="xsd:IDREF" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
Allegato 5: XML stylesheet per l’entità città
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document: city2.xsl
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes" />
<xsl:attribute-set name="date">
<xsl:attribute name="year">2004</xsl:attribute>
<xsl:attribute name="month">03</xsl:attribute>
<xsl:attribute name="day">19</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="tourGuide">
<xsl:processing-instruction name="xsl-stylesheet"> href="style.css" type="text/css"<br />
</xsl:processing-instruction>
<xsl:comment>This is a list of the cities we are visiting this week</xsl:comment>
<xsl:for-each select="city">
<!-- element name crea un nuovo elemento dove il valore dell’attributo name imposta il nome del
nuovo elemento. Nello stesso elemento può essere impostato più di un elemento. -->
<!-- l’attributo use-attribute-sets aggiunge tutti gli attributi dichiarati in precedenza in attribute-set -->
<xsl:element name="cityList" use-attribute-sets="date">
<xsl:element name="city">
<xsl:attribute name="country">
<xsl:apply-templates select="country"/> </xsl:attribute>
<xsl:apply-templates select="cityName"/>
</xsl:element>
<xsl:element name="details">Will write up a one page report of the trip</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
- Sebbene l’
output method
è impostato a "xml", fino a quando non c’è il tag<html>
come radice dell’albero risultato, l’output di default sarà XML. attribute-set
è un elemento top-level che crea un gruppo di attributi sotto il nome "date.".attribute set
può essere riusato in tutto il foglio di stile. L’elementoattribute-set
ha anche un attributo use-attribute-sets che consente di concatenare diverse serie di attributi.processing-instruction
crea il foglio di stile XML elaborando le istruzioni.- L’elemento
comment
crea un commento nell’albero risultato - L’elemento
attribute
consente di aggiungere un attributo a un elemento che è stato creato nell’albero risultato.
Il foglio di stile produce questo albero risultato:
Allegato 6: albero risultato XML per l’entità città
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!--
Document: city2.xsl
-->
<?xsl-stylesheet href="style.css" type="text/css"?>
<!--This is a list of the cities we are visiting this week-->
<cityList year="2004" month="03" day="19">
<city country="Belize">Belmopan</city>
<details>Will write up a one page report of the trip</details>
</cityList>
<cityList year="2004" month="03" day="19">
<city country="Malaysia">Kuala Lumpur</city>
<details>Will write up a one page report of the trip</details>
</cityList>
</stylesheet>
Il processore inserisce automaticamente la dichiarazione XML nella parte superiore della struttura ad albero risultato. L'istruzione processing-instruction
, o PI, è una direttiva di elaborazione che deve essere utilizzata da un’applicazione che la elabora. In questo caso, href indica un foglio di stile locale che verrà applicato al documento XML quando viene elaborato. Abbiamo usato <xsl:element> per creare un nuovo contenuto nell’albero risultato e gli abbiamo aggiunto degli attributi.
Ci sono altri due elementi istruzione per l'inserimento di nodi in un albero risultato. Si tratta di copy
e copy-of
. A differenza di apply-templates
, che copia solo il contenuto del nodo figlio (come il nodo di testo figlio), questi elementi copiano tutto. Il codice seguente mostra come l'elemento copy può essere utilizzato per copiare l'elemento città in city.xml:
Allegato 7: elemento copy
<xsl:template match="city">
<xsl:copy />
</xsl:template>
Questo è il risultato:
Allegato 8: risultato dell’elemento copy
<?xml version="1.0" encoding="utf-8">
<city />
<city />
L'output non è molto interessante, perché copy non prende i nodi figlio, solo il nodo corrente. Nel nostro esempio, raccoglie i due nodi città che si trovano nel file city.xml. L'elemento copy ha un attributo facoltativo, use-attribute-set, che consente di aggiungere attributi all'elemento.
Se si desidera copiare più di un solo nodo dal file di origine, l’elemento copy-of include il nodo corrente, e gli eventuali nodi attributo ad essi associati. Questo include ad esempio i nodi del namespace, nodi di testo e nodi figlio. Quando applichiamo copy ad un elemento di city.xml, il risultato è quasi una replica esatta di city.xml! È anche possibile copiare i commenti e le direttive di elaborazione con <xsl:copy-of select="comment()"/> e <xsl:copy-of select="processing-instruction(name)"/> dove name è il valore della attributo name nella direttiva di elaborazione che si desidera recuperare.
Perché questo sarebbe utile, vi chiederete? Ad esempio, se si desidera inserire una copia di city.xml in un pacchetto SOAP, si può facilmente farlo usando copy-of. Se non si conosce già, Simple Object Access Protocol, o SOAP, è un protocollo per il confezionamento di documenti XML per lo scambio. Questo è molto utile in un ambiente B2B perché fornisce un modo standard per impacchettare messaggi XML. Si può leggere di più su SOAP a www.w3.org/tr/soap.
Template
modificaSiccome i template definiscono le regole per la trasformazione dei nodi, sarebbe sensato riutilizzarli, sia nello stesso foglio di stile che in altri fogli di stile. Questo può essere realizzato dando un nome a un template, e quindi richiamandolo con un elemento call-template. Template denominati possono essere inclusi anche richiamandoli da altri fogli di stile. Ecco un esempio che utilizza i template denominati:
Allegato 9: I template denominati
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" />
<xsl:template match=" /">
<xsl:call-template name="getCity" />
</xsl:template>
<xsl:template name="getCity">
<xsl:copy-of select="city" />
</xsl:template>
</xsl:stylesheet>
I template hanno anche un attributo mode. Questo permette di elaborare un nodo più di una volta, producendo un risultato diverso di volta in volta, a seconda del template. Proviamo a metterlo in pratica:
Allegato 10: l’attributo mode dei template
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document: cityModes.xsl
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match="tourGuide">
<html>
<head>
<title>Città - Utilizza l’attributo mode</title>
</head>
<body>
<xsl:for-each select="city">
<xsl:apply-templates select="cityName" mode="title" />
<xsl:apply-templates select="cityName" mode="url" />
<br />
</xsl:for-each>
</body>
</html>
</xsl:template>
<xsl:template match="cityName" mode="title">
<h2><xsl:value-of select="current()"/></h2>
</xsl:template>
<xsl:template match="cityName" mode="message">
<p>Come visit <b><xsl:value-of select="current()" /></b>!</p>
</xsl:template>
</xsl:stylesheet>
apply-templates select="cityName" mode="title"
dice al processore di di cercare un template che abbia lo stesso valore per l’attributo mode.
value-of select="current()"
ritorna il nodo corrente, che è stato convertito in stringa convalue-of.
Anche utilizzandoselect="."
si avrà come valore di ritorno il nodo corrente.
Allegato 11: risultato del foglio di stile
<h2>Belmopan</h2>
Come visit <b>Belmopan</b>!
<h2>Kuala Lumpur</h2>
Come visit <b>Kuala Lumpur</b>!
Per impostazione predefinita, i processori XSLT sono dotati di regole di template. Se si applica un foglio di stile senza regole di matching, le regole predefinite vengono applicate automaticamente.