In quest‘articolo verrà realizzato un foglio di stile XSLT per produrre la pagina HTML che contiene un articolo di MokaByte. Nel fare questo verrà modificata leggermente la struttura dell‘XML previsto, per ottimizzarlo. Infine si svilupperà una classe Java in grado di eseguire la trasformazione XSLT, utilizzando gli strumenti open source Xerces e Xalan
XSLT ed XML. Oramai conosciamo tutti il significato di questi acronimi. Stanno rispettivamente per eXtendible Styles Language e eXtendible Maukup Language. Il primo è composto da un linguaggio di trasformazione (xslt) e uno di formattazione (xsl-fo).
Il linguaggio xslt (http://www.w3.org/Style/XSL/) permette la manipolazione vera e propria dei documenti, mentre il linguaggio xsl-fo permette la formattazione e l‘applicazione degli stili ai documenti. L‘obiettivo di questo articolo è di spiegare cosa verrà creato durante il parsing tra l‘xsl e l‘xml. Per spiegare cosa avviene durante la trasformazione abbiamo bisogno di un‘ esempio xsl e un‘esempio xml.
Il file xml di riferimento ha la struttura prevista per gli articolo di Mokabyte:
La comunicazione Java-Javascript Java accede al DOM [1] del Browser, ... Come si vedrà nel paragrafo successivo... Qui di seguito è riportato uno schema di...
import java.awt.*; class MyApplet extends Applet { public void init() { JSObject win = JSObject.getWindow(this); JSObject doc = (JSObject) win.getMember("document"); JSObject loc = (JSObject) doc.getMember("location"); String s = (String) loc.getMember("href"); win.call("f", null); // Call f() in HTML page } } ... ... ...
Ovviamente questo xml è solo una parte dell‘xml originale. È stato riformattato per essere più leggibile nell‘articolo.
Iniziamo a scrivere l‘XSL
In primo luogo è fondametale individuare l‘obbiettivo finale. In questo caso si vuole che l‘output prodotto sia codice HTML che rappresenti l‘articolo.
Successivamente bisogna stabilire i punti necessari al raggiungimento dell‘obbiettivo. Le regole possono essere così riassumibili:
- Ogni tag
e diventi un elemento di HTML.
- L‘elemento
deve diventare un‘elemento HTML
;
- E ogni elemento
deve essere trasformato nel famoso elemento
di HTML.
E‘ indispensabile sapere che un file xsl deve essere un documento XML ben formato.
Per questo motivo il foglio di stile deve iniziare con una dichiarazione xml corretta:
version indica la versione della tecnologia XML utilizzata. Un altro elemento importante in xsl è stylesheet:
La sua importanza è data dal fatto che il codice che ne conseguirà dovrà adattarsi a quelle che sono le regole di trasformazione e/o formattazione xslt.
Anche qui ritroviamo l‘attributo Version, il quale indica la versione xslt implementata dal processore. Si presenta un altro attributo nel tag stylesheet: xmlns:xsl, che in questo caso definisce il namespace "xsl".
Questo attributo dichiara al processore di che tipo sono gli elementi usati all‘interno del foglio di stile e tramite l‘url specificato identifica dove tali elementi sono definiti.
Inoltre esplicita che ogni elemento avrà un prefisso; in questo caso "xsl:" (vedi elemento stylesheet sopra citato).
Ulteriori esempi di namespace possono essere quelli riportati sotto
Il prossimo elemento è template match. Con questo elemento si è in grado di posizionarsi in qualunque punto dell‘xml.
Per esempio:
Assegnando il valore "/" (slash) all‘attributp match, accade che il processore quando esegue la trasformazione si posiziona sulla radice del documento xml avendo visibilità di tutti gli elementi figli contenuti in essa.
Sostituiendo "body" al volore assegnato a match, il processore si posiziona direttamente sul tag body. Ovviamente nell‘xml usato come esempio il tag body coincide con il tag radice, ma se al posto di body l‘attributo match fosse stato valorizzato con code, il processore si sarebbe posizionato direttamente sul tag code avendo visibilità solo dei nodi figli dell‘elemento code.
Infatti valorizzare l‘attributo match con il nome di un tag è utile se è necessario usare solo una parte dell‘xml a disposizione oppure se si desidera scrivere codice più compatto.
L‘inizio dell‘HTML
Verrà creato ora il primo elemento dell‘html utilizzando il linguaggio xsl. Per fare ciò si ha bisogno dell‘elemento xsl element:
Il risultato di questa istruzione sarà :
In questo modo si costruiranno anche il tag body, e ogni elemento necessario dell‘html.
Di seguito è riportato l‘xsl prodotto fino a ora:
...
Tornando all‘xml, si è detto che ogni tag paragraph e text devono diventare entrambi degli elementi
di html. Questo si ottiene con il seguente tag:
Con questa istruzione si esegue un ciclo. La descrizione corretta in italiano è: "Per ogni nodo figlio" oppure "per tutti i nodi figli".
Siccome i "figli" sono di tipo diverso, durante l‘esecuzione di un ciclo si deve capire su quale nodo è posizionato il processore. Per farlo si utilizza l‘elemento choose. Questo elemento permette di fare delle scelte multiple. In poche parole è l‘equivalente di un
if(...){...}else if(...){}else(...){...}
in java. Si prenda in esame la seguente parte di codice:
... ... ...
La funzione name() ritorna il nome del nodo corrente. Il test consiste nel verificare se il nome del nodo corrente corrisponde al tag paragraph. Se questa condizione è vera, verrà eseguita una specifica parte di codice. Contrariamente, verrà processata un‘altra parte di codice se il nome del nodo corrente non è uguale a paragraph ma a text.
Sia per l‘elemento text, sia per l‘elemento code quando saranno processati, sarà creato per ognuno un elemento p che racchiude il testo contenuto nell‘elemento corrente.
...
Si applica lo stesso ragionamento anche quando l‘elemento corrente è code ma creando anzichà© un elemento p un elemento html code. Si tenga presente che code contiene ulteriori nodi.
La seguente porzione di file XML è una rielaborazione dello schema originale. In questo formato sarà più facile sviluppare l‘xsl di formattazione del codice:
import java.awt.*; class MyApplet extends Applet { public void init() { JSObject win = JSObject.getWindow(this); JSObject doc = (JSObject) win.getMember("document"); JSObject loc = (JSObject) doc.getMember("location"); String s = (String) loc.getMember("href"); // document.location.href win.call("f", null); // Call f() in HTML page } }
Originariamente questa porzione di codice era così:
import java.awt.*; class MyApplet extends Applet { public void init() { JSObject win = JSObject.getWindow(this); JSObject doc = (JSObject) win.getMember("document"); JSObject loc = (JSObject) doc.getMember("location"); String s = (String) loc.getMember("href"); // document.location.href win.call("f", null); // Call f() in HTML page } }
Utilizzando questo xsl per trattare la parte relativa al tag :
......
Si ottiene il risultato seguente:
...
Non viene quindi visualizzato nulla. Analizzando il codice sopra riportato si nota che si preleva solo il contenuto del tag break e non di code.
Un tentativo di correzione potrebbe essere il seguente:
......
Ma si ottiene ancora un risultato indesiderato:
...
import java.awt.*;class MyApplet extends Applet {public void init() {JSObject win = JSObject.getWindow(this);JSObject doc = (JSObject) win.getMember("document");JSObject loc = (JSObject) doc.getMember("location");String s = (String) loc.getMember("href"); // document.location.hrefwin.call("f", null); // Call f() in HTML page}}
E‘ essenziale capire su quale elemento ci si trova quando è eseguito un ciclo, e con l‘xsl riportato sopra si è in grado di farlo, ma non si è in grado di ottenere il risultato voluto.
Questo perché il ciclo eseguito è per tutti i figli del nodo code e quando trova il tag break deve sostituirlo con l‘elemento BR e poi prelevare il contenuto di code. Di conseguenza crea prima un elemento BR per ogni tag break processato e solo successivamente ne scrive il contenuto, visualizzando un grande spazio bianco prima del testo.
Una soluzione
Modificando l‘xml come già visto, diviene possibile capire quale nodo si sta processando e quindi di prenderne il suo contenuto. In questo modo il file XML diventa:
import java.awt.*;
class MyApplet extends Applet {
public void init() {
JSObject win = JSObject.getWindow(this);
JSObject doc = (JSObject) win.getMember("document");
JSObject loc = (JSObject) doc.getMember("location");
String s = (String) loc.getMember("href"); // document.location.href
win.call("f", null); // Call f() in HTML page
}
}
Il foglio di stile dunque produce un output con il seguente aspetto:
import java.awt.*;class MyApplet extends Applet {public void init() {JSObject win = JSObject.getWindow(this);JSObject doc = (JSObject) win.getMember("document");JSObject loc = (JSObject) doc.getMember("location");String s = (String) loc.getMember("href"); // document.location.hrefwin.call("f", null); // Call f() in HTML page}}
Di seguito è riportato l‘XSL completo:
Un occhio al codice
Per eseguire una trasformazione XSLT in Java è sufficiente utilizzare le API standard JAXP. La classe XmlTransform invoca, nel costruttore, la chiamata al metodo newInstance() sulla classe TransformerFactory. In questo modo si ottiene un TransformerFactory da utilizzare nel metodo di trasformazione.
Il metodo transform() ottiene un oggetto Transformer dalla factory tramite il metodo newTrasformer, a cui passa l‘XSL da utilizzare. Questo viene passato sottoforma di oggetto StreamSource. Questa classe dispone di molti costruttori che permettono di ottenere il foglio di stile da molte sorgenti (p.e. http, inputstream). In questo caso i dati vengono letti dalla memoria utilizzando un oggetto ByteArrayInputStream, che trasforma un array di byte in un flusso.
La trasformazione avviene tramite il metodo transform(), che si aspetta uno StreamSource per leggere il file XML ed uno per scrivere la risposta. Questa viene memorizzata in un ByteArrayOutputStream apposito, da cui poi vengono ottenuti i dati per la risposta, tramite l‘invocazione del metodo toString().
public class XmlTransform {TransformerFactory transformerFactory;public XmlTransform() {transformerFactory = TransformerFactory.newInstance(); }public String transform(String xml, String xsl) throws TransformerException {System.out.println("xml=" + xml);System.out.println("xsl=" + xsl);ByteArrayOutputStream resultStream = new ByteArrayOutputStream(); ByteArrayInputStream xmlStream = new ByteArrayInputStream(xml.getBytes());ByteArrayInputStream xslStream = new ByteArrayInputStream(xsl.getBytes());Transformer transformer = transformerFactory.newTransformer(new StreamSource(xslStream));transformer.transform(new StreamSource(xmlStream), new StreamResult(resultStream));return resultStream.toString();}}
Le chiamate di trasformazione possono sollevare una TransformerException in caso di problemi. Può infatti capitare che il contenuto dei fogli di stile non sia compilato correttamente o contenga errori nel formalismo XML.
Per funzionare, la classe richiede una implementazione delle API JAXP. In questo caso è stato utilizzato Xalan 2.6.0, reperibile sul sito xml.apache.org.
Conclusioni
In questo articolo si è visto come realizzare un foglio di stile per trasformare un documento XML in una pagina HTML, utilizzando le API JAXP. Si è visto come queste siano molto semplici da utilizzare, e come una piccola classe possa svolgere una funzionalità molto potente.
Si è partiti dal flusso XML che descrive un articolo di Mokabyte, che è però stato leggermente variato per renderlo meglio gestibile attraverso i fogli di stile XSL.