Lucrarea 2
Download: PDF ODT
Rezumat
- Principii
- Un document (arbore) XML este transformat cu ajutorul unui document XSL într-un document XHTML.
- Legătura dintre documentul XML şi fisierul XSL este făcută explicit în fişierul XML.
- Transformările se aplică nod cu nod (element cu element), conform şabloanelor descrise în fişierul XSL.
- Pentru referirea oricărui nod din arbore, se folosesc expresii de cale XPath.
- În contextul unui nod curent aflat în prelucrare, elementele sale (ca de altfel întreaga structură XML) sunt disponibile prin folosirea unui set de instrucţiuni de formatare, parte integrantă a XSL.
- Descrierea tipului documentului XML se face prin referirea în interiorul său la un fişier DTD ce conţine regulile formale pentru structura sa arborescentă.
- Cuvinte cheie
- XML, XSL, XHTML, DTD
- XSLT, XPath, XSL Formatting Objects
- xsl:stylesheet, xsl:template, xsl:for-each, xsl:value-of
- Axă: ".", "..", "@", child
- Predicate: operatori, position(), count(), number(), substring(), sum()
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
Introducere
Deoarece HTML foloseşte etichete predefinite, sensul acestora este bine înţeles de către navigator (web-browser), care va ştii să le afişeze. Adăugarea de stiluri diferite unui document HTML, cu ajutorul CSS, este simplă. Fiecare element poate fi afişat cu fontul sau sau în culoarea dorită, la poziţia sau cu dimensiunile dorite prin simpla atasare a unei "clase CSS".
Pentru că XML nu foloseşte etichete predefinite (putem defini propriile noastre etichete), sensul acestora nu este cunoscut, şi navigatorul nu ar şti să le afişeze. De aceea trebuie ca pe lângă documentul XML să existe şi un fişier care să descrie modul în care aceste elemente vor fi afişate. Este vorba de un document XSL.
XSL constă din trei părţi:
- XSLT, un limbaj de transformare a documentelor XML;
- XPath, un limbaj de definire a parţilor unui document XML;
- XSL Formatting Objects, un set de instrucţiuni de formatare a unui document XML.
Modul în care procesul funcţionează este următorul: documentul XML este transformat cu ajutorul XSLT într-un document XHTML prin înlocuirea etichetelor XML cu etichete XHTML, sau prin preluarea datelor stocate în documentul XML şi integrarea lor în documentul XHTML. Rezultatul poate fi destinat monitorului unui calculator personal, ecranului unui dispozitiv mobil (pagini WAP de exemplu) sau trimis la imprimantă. În continuare ne vom axa pe XSLT şi pe XPath.
XSLT – XSL Transformations
XSLT este cea mai importantă parte a standardului XSL. Este partea care transformă un document XML în alt document XML sau în alt tip de document care este recunoscut de navigator. În mod normal XSLT realizează aceasta prin transformarea fiecărui element XML într-un element XHTML. XSLT poate de asemenea să adauge noi elemente în documentul rezultat sau poate să elimine elemente. Poate rearanja sau sorta elementele, poate testa condiţii şi poate decide ce elemente să fie afişate sau nu. Procesul de transformare este referit uzual ca transformare a unui arbore sursă într-un arbore destinaţie. În procesul de transformare, XSLT foloseşte XPath
pentru a defini părţi ale documentului sursă care se potrivesc cu nişte şabloane predefinite. Când a fost detectată o potrivire, XSLT transformă partea din documentul sursă care se potriveşte cu conţinutul şablonului definit. Părţile din documentul sursă care nu se potrivesc cu nici un şablon vor rămâne nemodificate în documentul rezultat. Să presupunem că avem următorul document XML (cds.xml) pe care dorim să-l transformăm. El constă dintr-o colecţie de CD-uri despre care ştim printre altele numele artistului, titlul albumului, anul de lansare şi preţul său. Dorim să-l transformăm într-o pagină HTML pentru a-l putea vizualiza cu ajutorul unui navigator.
Conţinutul unei colecţii de CD-uri, fişier cds.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<!-- more cd items...-->
<cd>
<title>Unchain my heart</title>
<artist>Joe Cocker</artist>
<country>USA</country>
<company>EMI</company>
<price>8.20</price>
<year>1987</year>
</cd>
</catalog>
Următorul lucru pe care ar trebuie să-l facem ar fi să creăm un document XSL care să formateze documentul XML de mai sus. Elementul care declară un document XSL este <xsl:stylesheet> sau <xsl:transform>. Cele două exprimări sunt sinonime şi poate fi folosită oricare dintre ele.
Declaraţia corectă a documentului în conformitate cu recomandările standardului XSL este:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
sau
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Să presupunem că am creat următorul document XSL (catalog.xsl) care conţine un şablon:
Macheta pentru colecţia de CD-uri, fişier catalog.xsl
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th align="left">Title</th>
<th align="left">Artist</th>
</tr>
<xsl:for-each select="catalog/cd">
<tr>
<td>
<xsl:value-of select="title"/>
</td>
<td>
<xsl:value-of select="artist"/>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Pentru a putea interacţiona, cele două documente trebuie să fie legate în următorul mod:
Conţinutul unei colecţii de CD-uri, fişier cds.xml legat la un XSL
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="catalog.xsl"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<!-- more entries -->
<cd>
<title>Unchain my heart</title>
<artist>Joe Cocker</artist>
<country>USA</country>
<company>EMI</company>
<price>8.20</price>
<year>1987</year>
</cd>
</catalog>
Se observă că referinţa există doar în interiorul documentului XML. Aceasta permite ca un document XML să poată folosi diferite documente de transformare doar prin simpla modificare a acestei referinţe. Schimbarea poate fi făcută şi automat, de exemplu atunci când folosim
dispozitive de afişare diferite (telefon mobil, monitor, proiector, imprimantă) prin specificarea atributului media în cadrul directivei <?xml-stylesheet?>.
Elementul <xsl:template>
Pentru a putea explica rezultatul obţinut cu ajutorul documentului XSL de mai sus
(catalog.xsl) trebuie în primul rând să explicăm fiecare linie în parte.
Astfel, prima linie: <?xml version="1.0" encoding="ISO-8859-1"?> defineşte documentul ce urmează ca fiind un document XML. Asta înseamnă că toate restricţiile şi toate regulile ce au fost definite pentru documentele XML se aplică şi aici. În fond, un document XSL este un document XML cu un set de etichete mai speciale şi care este interpretat într-un mod standard.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Linia de mai sus defineşte documentul XSL. Dacă linia de mai sus ar fi omisă, atunci am obţine un mesaj de eroare care spune că navigatorul nu ştie prefixul de etichetă <xsl:... >.
Rolul acesteia reiese mai bine dacă ne uităm şi la sintaxa elementului.
Sintaxa xsl:stylesheet
<xsl:stylesheet id="nume" version="versiune" extension-element-prefixes="lista" exclude-result-prefixes="lista">
<!-- Continut -->
</xsl:stylesheet>
Atribute:
- version (necesar) – specifică versiunea documentului XSLT.
- id (opţional) – specifică un identificator unic pentru document.
- extension (opţional) – specifică o listă a prefixelor folosite pentru elementele de extensie.
- exclude (opţional) – specifică o listă a elementelor care nu vor fi trimise la ieşire.
Linia următoare conţine elementul responsabil pentru obţinerea documentului HTML prin potrivirea întregului document XML:
<xsl:template match="/">
Pe scurt elementul de mai sus reprezintă un şablon XSL care are rolul de a transforma anumite părţi ale documentului XML. Părţile ce vor fi transformate sunt specificate prin valoarea atributului match. Valorile atributului reprezintă expresii de tip XPath şi au rolul de a specifica
elemente ale unui document XML. Conţinutul acestui element (xsl:template) este chiar ceea ce se va trimite spre ieşire.
XPath
Înainte de a prezenta modul în care acţionează elementul <xsl:template> trebuie să facem o scurtă incursiune în XPath. Am precizat mai sus că rolul acestuia este de a specifica părţi ale unui document XML. Cu ajutorul lui informaţia dintr-un document XML poate fi identificată şi apoi transformată de XSLT.
Când lucrăm cu XSLT, contextul unui element este nodul (ca element al arborelui) din documentul XML aflat în procesare. Deci în interiorul elementului <xsl:template match="/"> contextul este nodul rădăcină, părinte pentru <catalog>. Setarea contextului poate fi realizată cu ajutorul expresiilor de cale.
Dintre cele mai folosite enumerăm:
- element – selectează toţi descendenţii nodului care reprezintă contextul curent şi care sunt denumiţi element. De exemplu “cd” selectează toate elementele <cd> care sunt descendente ale nodului context curent.
- / – selectează începând de la nodul rădăcină al documentului sau de la nodul context curent. De exemplu “/” selectează rădăcina documentului, “/cd” selectează elementele <cd> pornind de la rădăcina documentului, “cd/title” selectează toate elementele <title> care sunt descendente ale elementului <cd>.
- // – selectează noduri în document indiferent de poziţie. Indiferent de context, el identifică noduri descendente cu oricâte niveluri intermediare. De exemplu “//artist” identifică elementele <artist> indiferent unde se află în document, “cd//artist” identifică toate elementele <artist> care sunt descendente (deci nu neapărat direct) ale elementului <cd>.
Despre alte expresii XPath precum şi de operatori sau funcţii XPath vom mai vorbi pe parcurs.
Elementul <xsl:template> conţine reguli care vor fi aplicate atunci când se găseşte o potrivire a modelului în documentul sursă.
Sintaxa xsl:template
<xsl: template name="nume" match="model" mode="mod" priority="numar">
<!-- Contnut-->
</xsl: template >
Atribute:
- name (opţional) – specifică numele şablonului. Dacă acesta lipseşte atunci trebuie să fie prezent atributul match.
- match (opţional) – specifică un model asupra căruia se va aplica şablonul. Dacă acesta lipseşte atunci trebuie să fie prezent atributul name.
- mode (opţional) – specifică un mod al şablonului.
- priority (opţional) – specifică prioritatea şablonului.
Elementul <xsl:value-of>
În macheta exemplu de mai sus, pe lângă elementul <xsl:template> se observă încă două elemente: <xsl:value-of> şi <xsl:for-each>. Despre <xsl:for-each> vom spune deocamdată că el se comportă la fel ca o buclă for. Celălalt element se comportă ca un operator. El va selecta valoarea elementelor <title> şi <artist> şi le va trimite la ieşire. Trebuie să observăm că prin execuţia elementului <xsl:for-each> contextul se mută pe fiecare element de tip <cd> şi deci accesarea elementelor <artist> şi <title> se poate face direct.
Să operăm următoarea schimbare în documentul catalog.xsl:
- Modificare în catalog.xsl
<!-- <xsl:for-each select="catalog/cd"> -->
<tr>
<td>
<xsl:value-of select="catalog/cd/title"/>
</td>
<td>
<xsl:value-of select="catalog/cd/artist"/>
</td>
</tr>
<!-- </xsl:for-each> -->
Se observă ca elementul <xsl:for-each> a fost scos prin comentare şi elementele <xsl:value-of> au fost modificate. Accesarea elementelor <title> şi <artist> a trebuit făcută prin specificarea absolută a contextului. Un lucru în plus este faptul că acum ieşirea se constituie doar din prima linie a documentului XML. Dacă de exemplu scriam:
- Modificare în catalog.xsl
<!-- <xsl:for-each select="catalog/cd"> -->
<tr>
<td>
<xsl:value-of select="catalog/cd"/>
</td>
<td>
<xsl:value-of select="catalog/cd/artist"/>
</td>
</tr>
<!-- </xsl:for-each> -->
atunci prima coloană ar fi conţinut valoarea primului element <cd>.
Sintaxa xsl:value-of
<xsl:value-of select="expresie" disable-output-escaping="yes|no"/>
Atribute:
- select (necesar) – expresie XPath care specifică de la care nod/atribut să extragă valoarea.
- disable (opţional) – valoarea “yes” va impune ca la ieşire caracterele speciale ca “>” să fie trimise ca atare. Valoarea “no” impune ca acestea să fie trimise de exemplu ca “>”. Valoarea implicită a atributului este “no”.
Elementul <xsl:for-each>
După cum am văzut mai sus, elementul <xsl:for-each> realizează o iterare a nodurilor dintr-o mulţime specificată. În exemplul dat se selecta pe rând fiecare nod <cd> şi se construia pornind de la acesta un rând în tabelul de ieşire. Operaţii adiţionale asupra iterării se pot face utilizând diferiţi operatori XPath.
Sintaxa xsl:for-each
<xsl:for-each select="expresie">
<!-- Continut -->
</xsl:for-each>
Atribut:
- select (necesar) – expresie XPath care specifică nodurile ce trebuie selectate. Să presupunem că vrem ca documentul nostru rezultat să conţină doar albumele apărute după anul 1990. Ceea ce va trebui să schimbăm în documentul XSL este expresia de selectare de la elementul <xsl:for-each>.
- Modificare în catalog.xsl
<xsl:for-each select="catalog/cd[year>1990]">
Axa
A venit momentul să prezentăm şi alte componente ale standardului XPath. Trebuie să vorbim în primul rând de un lucru pe care l-am folosit fără însă a ştii ce reprezintă: axa. O axă este o expresie XPath care returnează o listă de noduri bazându-se pe nodul context curent. Iată în continuare câteva exemple:
- self – forma abreviată este “.”. Această expresie selectează nodul context curent. Exemplu:<xsl:value-of select="."/>. Dacă nodul curent conţine o valoare textuală aceasta este trimisă la ieşire.
- ancestor – selectează calea (secvenţa de noduri intermediare) pornind de la părintele nodului context curent până la nodul rădăcină.
- parent – forma abreviată este “..”. Selectează părintele nodului context curent.
- attribute – forma abreviată este “@”. Acesta selectează toate atributele unui nod. De exemplu<xsl:value-of select="@ID"/> selectează atributul ID al elementului context curent.
- child – acesta este cel implicit în forma abreviată. El selectează toţi descendenţii nodului curent.
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist rank="1">Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist rank="4">Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<!-- more cd items...-->
<cd>
<title>Unchain my heart</title>
<artist rank="2">Joe Cocker</artist>
<country>USA</country>
<company>EMI</company>
<price>8.20</price>
<year>1987</year>
</cd>
</catalog>
Exemplu de citire a atributelor rank parcurgând calea catalog/cd :
<xsl:value-of select="artist/@rank"/>
Predicate
Am văzut că o axă ne ajută să găsim o listă de noduri pornind de la nodul context curent. Un predicat ne ajută să impunem anumite constrângeri unei expresii XPath. Un exemplu de predicat am folosit mai sus pentru a selecta doar albumele apărute după 1990.
Sintaxa predicat
[expresie]
Atribut:
- expresie – expresia predicatului. Ea va conţine condiţiile impuse.
De exemplu:
- Modificare în catalog.xsl
<xsl:for-each select="catalog/cd[position() mod 2 = 0]">
va selecta doar nodurile a căror poziţie este pară. Funcţia position() returnează poziţia curentă a nodului curent in lista selectată. Pentru a crea diferite condiţii se pot aplica diferiţii operatori. Cu doi dintre ei ne-am întâlnit deja.
Trebuie totuşi să-i enumerăm pe toţi:
- and, or – operaţiile logice ŞI, SAU.
- = – egalitate
- != – diferit de
- >, >= – mai mare, mai mare sau egal
- <, <= – mai mic, mai mic sau egal
- +, -, *, div – adunare, scădere, înmulţire, împărţire
- mod – restul împărţirii întregi
- | – reunirea a două mulţimi de noduri
O observaţie importantă ce trebuie făcută este aceea de a codifica caracterul < prin entitatea < deoarece se va considera că în acel punct va începe un nou element şi nu operator. Analog, > se înlocuieşte cu >.
Funcţia position()
Pe lângă operatorii de mai sus se mai pot utiliza şi funcţii XPath. Una dintre ele am văzut-o mai sus: position(). Ea returnează poziţia curentă a nodului context curent relativ la mulţimea nodurilor din care face parte.
De exemplu porţiunea de cod de mai jos va adăuga încă o coloană conţinând poziţia curentă:
- Modificare în catalog.xsl
<xsl:for-each select="catalog/cd[year>1990]">
<tr>
<td>
<xsl:value-of select="position()"/>
</td>
<td>
<xsl:value-of select="title"/>
</td>
<td>
<xsl:value-of select="artist"/>
</td>
</tr>
</xsl:for-each>
Se observă că aceste valori sunt relative la selecţia făcută şi nu la poziţia lor în cadrul documentului sursă.
Funcţia count()
O altă funcţie utilă este funcţia count(). Ea returnează numărul de noduri conţinută într-o mulţime dată. De exemplu expresia de mai jos trimite la ieşire numărul de elemente <cd> din document:
- Modificare în catalog.xsl
<td>
<xsl:value-of select="count(/catalog/cd)"/>
</td>
Funcţia number()
Această funcţie converteşte orice valoare dată ca parametru la un număr. Dacă parametrul dat este un şir de caractere atunci va returna NaN.
Funcţia substring()
Această funcţie returnează o porţiune a unui şir de caractere pe baza parametrilor daţi:
Sintaxa substring:
substring( valoare, start )
substring( valoare, start, lungime )
Atribute:
- valoare – şirul de caractere din care se va face extragerea.
- start – poziţia de start din care se face extragerea. Şirul de caractere din care se face extragerea are primul caracter pe poziţia 1.
- lungime – numărul de caractere extrase.
Exemplul următor selectează doar artiştii al căror nume începe cu litera ‘B’:
- Modificare în catalog.xsl
<xsl:for-each select="catalog/cd[substring(artist, 1, 1) = 'B']">
Funcţia sum()
Această funcţie returnează suma valorilor unei mulţimi de elemente date. O
cerinţă importantă este ca toate acestea să aibă valori numerice. De exemplu
suma tuturor albumelor selectate se poate obţine prin:
- Modificare în catalog.xsl
<xsl:value-of select="sum(//cd[substring(artist, 1, 1) = 'B']/price)"/>
TEMĂ
1. Creaţi un document numit inbox.dtd cu conţinutul de mai jos:
Definiţia tipului documentului XML, fişier inbox.dtd
<!ELEMENT inbox (mail)*>
<!ELEMENT mail (to, cc?, bcc?, from, subject, data)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT cc (#PCDATA)>
<!ELEMENT bcc (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT subject (#PCDATA)>
<!ELEMENT data (#PCDATA)>
2. Pornind de la documentul de mai sus creaţi un document XML numit inbox.xml care să respecte restricţiile.
3. Creaţi un document XSL numit inbox.xsl care să afişeze documentul XML de mai sus sub forma unui tabel cu 5 coloane (to, cc, bcc, from, subject) în care să afişaţi fiecare nod <mail>.
4. Modificaţi documentul XSL de mai sus astfel încât să fie afişate în tabel doar mesajele primite cu un anumit subiect.
5. Afişaţi numărul de mesaje primite în total de la o anumită persoană.