18
XSLT XSLT este limbajul de transformare a documentelor XML și se bazează pe Xpath pentru a extrage informații dintr-un document sursă și a le pune într-un document rezultat (cu structură diferită). De obicei XSLT e folosit pentru a genera pagini HTML din date XML, deci poate fi folosit și ca instrument AJAX (conversia răspunsului de la server în cod HTML). Pornim de la următorul document XML (salvat cu numele comanda.xml) <?xml version="1.0" encoding="UTF-8"?> <comanda> <!-- acesta e un comentariu --> <produs denumire="Televizor" id="p1" pret="100"/> <produs id="p2" pret="200">Calculator</produs> <produs> <id>p3</id> <pret>300</pret> <denumire>Ipod</denumire> </produs> <prettotal>600</prettotal> Aceasta este o comanda de produse </comanda> Creăm în Oxygen următoarea foaie XSLT (salvată cu numele transformare.xsl): <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/">Text oarecare</xsl:template> </xsl:stylesheet> O foaie XSLT conține una sau mai multe reguli de substituire cu două componente: <xsl:template match="/"> Text oarecare </xsl:template> Exemplul de față selectează întreg documentul (calea XPath "/" reprezintă documentul în ansamblul său) și îl înlocuiește cu stringul "Text oarecare". Pentru a executa transformarea, Oxygen impune să se creeze un scenariu de transformare (diverse configurări). match este calea Xpath a elementelor care vor suferi substituția conținutul lui xsl:template este noul conținut care va intra în locul celui detectat de match.

Seminar 8 UIA 2015

  • Upload
    lr

  • View
    230

  • Download
    0

Embed Size (px)

DESCRIPTION

uia

Citation preview

Page 1: Seminar 8 UIA 2015

XSLT XSLT este limbajul de transformare a documentelor XML și se bazează pe Xpath pentru a extrage informații dintr-un document sursă și a le pune într-un document rezultat (cu structură diferită). De obicei XSLT e folosit pentru a genera pagini HTML din date XML, deci poate fi folosit și ca instrument AJAX (conversia răspunsului de la server în cod HTML). Pornim de la următorul document XML (salvat cu numele comanda.xml) <?xml version="1.0" encoding="UTF-8"?> <comanda> <!-- acesta e un comentariu --> <produs denumire="Televizor" id="p1" pret="100"/> <produs id="p2" pret="200">Calculator</produs> <produs> <id>p3</id> <pret>300</pret> <denumire>Ipod</denumire> </produs> <prettotal>600</prettotal> Aceasta este o comanda de produse </comanda> Creăm în Oxygen următoarea foaie XSLT (salvată cu numele transformare.xsl): <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/">Text oarecare</xsl:template> </xsl:stylesheet> O foaie XSLT conține una sau mai multe reguli de substituire cu două componente: <xsl:template match="/"> Text oarecare </xsl:template> Exemplul de față selectează întreg documentul (calea XPath "/" reprezintă documentul în ansamblul său) și îl înlocuiește cu stringul "Text oarecare". Pentru a executa transformarea, Oxygen impune să se creeze un scenariu de transformare (diverse configurări).

match este calea Xpath a elementelor care vor suferi substituția

conținutul lui xsl:template este noul conținut care va intra în locul celui detectat de match.

Page 2: Seminar 8 UIA 2015

Crearea scenariului e solicitată când încercăm să executăm transformarea.

Tipul scenariului va fi XSLT transformation dacă documentul activ din Oxygen e chiar transformarea (dacă e XMLul original, alegem tipul XML transformation with XSLT). Creăm un scenariu nou.

Page 3: Seminar 8 UIA 2015

Setările esențiale sunt în prima și ultima categorie. Categoria FO Processor se folosește doar dacă facem transformări de tip XSL-FO (o alternativă la CSS, dedicată formatării de cod XML). Avem deja completată calea foii XSLT curente (codul currentFileURL e suficient). Trebuie să selectăm manual adresa XMLului original. În acest fel am precizat ce se va transforma (comanda.xml) și cum se va transforma (foaia curentă din Oxygen). Mai trebuie să precizăm ce să se întâmple cu rezultatul transformării, în categoria Output:

Decidem să salvăm fișierul cu numele rezultat.xml și să deschidem imediat rezultatul în Oxygen. Apoi apăsăm OK și Transform now. Rezultatul va fi: <?xml version="1.0" encoding="utf-8"?>Text oarecare

Page 4: Seminar 8 UIA 2015

Se observă că nu e obligatoriu ca XSLT:

• să returneze documente XML bine formate; • să returneze bucăți din documentul original (poate să-l substituie complet cu orice, dar de

obicei rostul lui XSLT e să extragă anumite date de interes din sursă și să le reorganizeze într-o structură nouă, de obicei o pagină HTML).

O concluzie importantă este că XSLT nu conservă implicit conținutul fișierului original! Dacă nu punem nimic în regula de substituire, pur și simplu se returnează un document gol. Dacă vrem să conservăm întocmai unele elemente din fișierul original, trebuie să definim explicit o regulă de conservare (=o regulă care substituie acele elemente cu ele însele). Revenim mai târziu la regulile de conservare. Obs: Dacă outputul transformării este un fișier XML, caracterele invizibile (spațiu, Tab, Enter) din interiorul regulilor se conservă. Dacă outputul e HTML, acestea se ignoră. Deci pe un rezultat XML nu e totuna dacă regula e: <xsl:template match="/>Text oarecare</xsl:template> sau <xsl:template match="/> Text oarecare </xsl:template>

Reguli de substituire iterative Substituirile iterative sunt printre cele mai populare în XSLT, pentru că permit să se parcurgă (cu un ciclu FOR) toate rezultatele unei interogări XPath și să se aplice o transformare fiecăreia dintre acestea. Adesea ele sunt îmbinate cu regulile de substituire alternativă (IF și CASE) care aplică substituții diferite în funcție de o interogare XPath booleană. În exemplul următor, pentru fiecare produs din COMANDA se va returna cuvântul Produs scris cu italic (într-o pagină HTML): <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <xsl:for-each select="comanda/produs">

<i>Produs</i> </xsl:for-each>

</xsl:template> </xsl:stylesheet>

Page 5: Seminar 8 UIA 2015

Obs:

• de data aceasta generăm cod HTML; • am bifat și opțiunea de afișare în browser a rezultatului salvat.

Rezultat: <?xml version="1.0" encoding="utf-8"?><i>Produs</i><i>Produs</i><i>Produs</i> Modificăm exemplul pentru a afișa cuvintele unul sub celălalt și pentru a concatena o numerotare după fiecare (Produs0, Produs1 etc.), adică echivalentul unui ciclu FOR de genul următor (dacă am fi în PHP): for ($i=0;$i<3;$i++) print "<i>Produs</i>".$i."<br/>"; O problemă spinoasă pentru începători e modul în care XSLT tratează conceptul de variabilă. Într-o anumită măsură pot fi considerate constante, în sensul că li se poate atribui o singură dată o valoare - dar acea valoarea poate să varieze, dacă e rezultată dintr-o interogare XPath în loc să fie o valoare fixată. Aceasta ne împiedică să scriem un ciclu FOR similar celui de mai sus (care necesită ca lui $i să își schimbe valoarea de mai multe ori). Inițializarea unei variabile se realizează cu una din construcțiile: <xsl:variable name="i" select="0"/> (dacă valoarea este una simplă, indiferent că e fixată sau rezultată dintr-o interogare XPAth - care va apare în select, în locul lui 0) <xsl:variable name="i">

<i>Produs</i> </xsl:variable> (dacă valoarea este cod XML complex) În funcție de conținutul variabilei, apelarea sa se face diferit: <xsl:value-of select="$i"/>

Page 6: Seminar 8 UIA 2015

(dacă valoarea e simplă sau dacă e cod XML din care vrem să luăm doar conținutul textual) <xsl:copy-of select="$i"/> (dacă valoarea e cod XML și vrem să-l preluăm integral, cu tot cu marcatori) Rostul uzual al variabilelor este să asigure reutilizarea unui fragment de cod XML. De exemplu precedenta transformare poate să aibă și următoarea formă: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:variable name="i"> <i>Produs</i> </xsl:variable> <xsl:template match="/"> <xsl:for-each select="comanda/produs"> <xsl:copy-of select="$i" /> </xsl:for-each> </xsl:template> </xsl:stylesheet> Începătorii sunt tentați să ignore faptul că variabilelor XSLT nu li se pot reatribui valori, și vor încerca să implementeze un ciclu FOR care să afișeze Produs0, Produs1, Produs2 astfel: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:variable name="i" select="0"/> <xsl:template match="/"> <xsl:for-each select="comanda/produs"> <i>Produs</i> <xsl:variable name="i" select="$i+1"/> <xsl:value-of select="$i"/> <br/> </xsl:for-each> </xsl:template> </xsl:stylesheet> Problema e că această linie nu se execută niciodată, deoarece variabila/constanta $i a fost deja inițializată. Rezultatul va fi afișarea repetată a lui Produs1. Totuși, cum putem ajunge la rezultatul dorit? Secretul stă în faptul că atributul select poate avea ca valoare ORICE interogare Xpath (se vede cum e folosit la xsl:for-each). Tot ce trebuie să facem e să pasăm responsabilitatea contorizării spre Xpath. Practic, nu dorim decât să facem o numărătoare a produselor. Dacă vă mai amintiți din XPath, poziția unui element între frații săi se obține prin numărarea fraților precedenți. count(preceding-sibling::produs) (calea e relativă la fiecare produs în parte, acestea fiind deja selectate de ciclul FOR). Deci transformarea corectă este:

Page 7: Seminar 8 UIA 2015

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <xsl:for-each select="comanda/produs"> <i>Produs</i> <xsl:value-of select="count(preceding-sibling::produs)"/> <br/> </xsl:for-each> </xsl:template> </xsl:stylesheet> La executarea ei, se vede că apar cuvintele Produs0, Produs1, Produs2 unul sub altul (am concatenat și un BR după număr). Rețineți acest truc și faptul că sunt numeroase situațiile în care lipsa de flexibilitate a variabilelor din XSLT poate fi compensată prin generarea unor valori variabile cu funcții Xpath. În general linia <xsl:value-of select=".........."/> este una din cele mai des întâlnite din XSLT. Cu ajutorul ei se pot genera:

• atât valori calculate în XSLT (cu variabile); • cât și valori extrase cu Xpath din documentul original.

Obs:Mai remarcați și faptul că adăugarea de conținut nou (HTML în acest caz) se face prin simpla scriere de cod nou în interiorul lui xsl:template, fără să fie necesar un operator de concatenare explicit!

Reguli de substituire recursivă

Atunci când nici Xpath nu ne ajută în a depăși problema rigidității variabilelor, mai avem o soluție la îndemână – regulile recursive. O regulă recursivă e o regulă ce se apelează pe ea însăși, de obicei cu parametri. Mecanismul e similar cu funcțiile recursive din alte limbaje: regula XSLT se comportă ca o funcție, parametrii ei se comportă ca argumente ale unei funcții. Problema cu numărătoarea produselor se poate rezolva și astfel:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <xsl:call-template name="generare"> <xsl:with-param name="listaproduse" select="comanda/produs"/> </xsl:call-template> </xsl:template> <xsl:template name="generare"> <xsl:param name="i" select="0"/>

Page 8: Seminar 8 UIA 2015

<xsl:param name="listaproduse"/> <xsl:if test="$listaproduse"> <i>Produs<xsl:value-of select="$i"/></i> <xsl:call-template name="generare"> <xsl:with-param name="i" select="$i+1"/> <xsl:with-param name="listaproduse" select="$listaproduse[position()>1]"/> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet> Observați că e vorba de 2 reguli:

• prima are match="/" și are rolul de a aplica efectiv substituirea documentului; • a doua nu are match, dar are name, deci poate fi apelată ca o funcție.

Un apel de "funcție" e format din două componente: • xsl:call-template indică numele funcției apelate; • xsl:with-param indică numele și valoarea argumentelor cu care se apelează funcția (în

interiorul funcției, acestea trebuie declarate cu xsl:param și cu aceleași nume ca și argumentele din apel).

De exemplu secvența (din prima regulă) de mai jos <xsl:call-template name="generare"> <xsl:with-param name="listaproduse" select="comanda/produs"/> </xsl:call-template> s-ar traduce în programarea clasică prin construcții de forma (sintaxa e orientativă, nu aparține unui limbaj anume): listaproduse=<vectorul produselor din comanda> generare(listaproduse) în timp ce a doua regulă s-ar traduce prin: function generare(listaproduse,i=0) { if (listaproduse) write "Produs"+i i=i+1 listaproduse=<vectorul listaproduse fara primul element> generare(listaproduse,i) } Observați diferența între modul de lucru al variabilelor și modul de lucru al parametrilor. Parametrii își pot schimba valoarea la apelul unei "funcții" deci nu sunt la fel de rigizi ca variabilele! De aceea putem simula cicluri FOR prin funcții care se apelează pe ele însele cu un argument $i care tot crește la fiecare apel. Aici parametri/argumentele sunt:

• $i, inițializat cu zero la primul apel și incrementat apoi la fiecare apel ce urmează; • $listaproduse, inițializată cu vectorul produselor la primul apel, și modificată prin eliminarea

treptată a câte unui produs, la apelurile următoare, până se golește (care e și condiția de terminare).

Page 9: Seminar 8 UIA 2015

Condiția de continuare a recursivității e <xsl:if test="$listaproduse">, adică oprim ciclul atunci când vectorul listaproduse rămâne gol (s-au terminat de eliminat produsele din el). Cum aminteam, recursivitatea se folosește adesea atunci când dorim să implementăm cicluri FOR în care o variabilă își tot schimbă valoarea, iar această schimbare nu poate fi pasată spre Xpath (de exemplu când calculăm produsul unor elemente, căci nu există funcția "product" în Xpath1).

Reguli de substituire alternativă Observați că până aici am generat un document nou fără să folosim conținutul existent în cel original (nu chiar deloc, am folosit poziția produselor în ultimul exemplu). Rolul uzual al unei transformări XSLT este să genereze cod și conținut nou concatenat cu cod și conținut din cel vechi. În următorul exemplu, în locul cuvintelor Produs0, Produs1, Produs2, vom genera denumirile produselor. Această sarcină e puțin complicată de faptul că documentul XML original are o structură neregulată – primul produs are denumirea într-un atribut, al doilea îl are în conținutul textual, al treilea îl are într-un element-fiu. Deci, chiar dacă parcurgem cu un ciclu FOR cele 3 produse, va trebui să implementăm o structură CASE care să extragă denumirea de la caz la caz: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <xsl:for-each select="comanda/produs"> <xsl:choose> <xsl:when test="@denumire"> <xsl:value-of select="@denumire"/> </xsl:when> <xsl:when test="denumire"> <xsl:value-of select="denumire"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="."/> </xsl:otherwise> </xsl:choose> <hr/> </xsl:for-each> </xsl:template> </xsl:stylesheet> Structura CASE e definită cu xsl:choose. Aceasta conține un șir de variante definite cu xsl:when (fiecare cu câte o condiție testată cu atributul test). Ultima variantă este xsl:otherwise (pentru cazul în care nici una din ramuri nu a funcționat).

1 XPath 1.0 are doar 2 funcții de agregare: sum și count.Restul operațiilor iterative (medie, produs, etc.) trebuie să aibă loc în limbajul care găzduiește Xpath. Xpath 2.0 în schimb permite să se implementeze cicluri FOR chiar în căi, făcând posibile astfel de operații iterative direct în căi Xpath. În general Xpath 2.0 și XSLT 2.0 simplifică mult exemplele discutate aici, însă le-am putea testa doar în Oxygen, sunt încă slab suportate în mediile de programare (iar în browsere deloc și nici nu s-au anunțat planuri de dezvoltare a browserelor în această direcție, tendința acestora fiind să consolideze rolul lui JavaScript, nu să îl substituie cu XSLT!).

Page 10: Seminar 8 UIA 2015

Ce face structura CASE? • Dacă găsește un atribut denumire (test="@denumire") îi extrage valoarea (cu xsl:value-of); • Dacă găsește un element denumire (test="denumire") îi extrage valoarea; • Dacă nu găsește nici una din variante (otherwise), va considera că denumirea se află în

conținutul textual, deci valoarea elementului ("." reprezintă nodul curent în Xpath). Observați că

• toate căile Xpath din atributele test și select sunt RELATIVE la nodul curent (produsul la care s-a ajuns cu xsl:for-each);

• la rândul său, atributul select de la for-each e o cale RELATIVĂ la nodul selectat de match. Rețineți că în general, XSLT folosește căi RELATIVE la nodul pe care s-a poziționat instrucțiunea-părinte (cea cu match e părinte pt for-each, acesta e părinte pt choose și when etc.). Asta cu excepția atributului match care folosește căi absolute (chiar dacă nu punem slash la începutul lor!) Structura CASE nu e singura metodă prin care putem obține cele 3 denumiri! După cum am văzut la exemplul cu numerotarea, XSLT conlucrează cu Xpath într-o manieră flexibilă – unele sarcini pot fi transferate spre Xpath, inclusiv filtrarea/selecția de elemente. Putem să transferăm responsabilitatea selecției și spre Xpath, cu același rezultat ca precedentul exemplu: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <xsl:for-each select="comanda/produs"> <xsl:value-of

select="self::node()[not(denumire) and not(@denumire)]|denumire|@denumire"/>

<hr/> </xsl:for-each> </xsl:template> </xsl:stylesheet> Observați cum am evitat structura CASE mutând decizia spre Xpath:

• self::node()[not(denumire) and not(@denumire)] returnează valoarea nodului curent cu condiția ca acesta să nu aibă un fiu denumire și nici un atribut denumire;

• denumire returnează valoarea fiului denumire (dacă există, evident); • @denumire returnează valoarea atributului denumire (dacă există).

Între cele 3 variante am pus operatorul Xpath | care returnează reuniunea soluțiilor celor 3 interogări. Cum fiecare PRODUS e doar în una din cele 3 situații, se va returna valoarea dorită pt. fiecare caz! O a treia variantă ar fi să evităm cu totul ciclul FOR (ceea ce nu e chiar recomandat, dar aici numărul de produse e suficient de mic încât să le putem selecta individual). <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/">

Page 11: Seminar 8 UIA 2015

<xsl:value-of select="comanda/produs[1]/@denumire"/> <hr/> <xsl:value-of select="comanda/produs[2]"/> <hr/> <xsl:value-of select="comanda/produs[3]/denumire"/> <hr/> </xsl:template> </xsl:stylesheet> A patra variantă mută iarăși o parte din sarcini spre Xpath: de data asta exploatăm atributul match al regulii – în loc să substituim tot documentul original, procedăm astfel:

• Îi substituim doar produsele (punând în match un Xpath care ne dă toate produsele); • Nodurile care nu sunt produse le ștergem (cu o regulă generală de substituire cu șirul vid).

Primul aspect e rezolvat astfel (observați cum match nu mai selectează rădăcina, ci produsele): <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/comanda/produs"> <xsl:value-of

select="self::node()[not(denumire) and not(@denumire)]|denumire|@denumire"/> <hr/> </xsl:template> </xsl:stylesheet> Dacă rulați această transformare, veți observa:

• Produsele se substituie; • Dintre restul nodurilor se conservă doar nodurile de tip text (inclusiv cele invizibile)! Aceasta

e o regulă implicită de funcționare în XSLT: orice nod care nu e afectat de nici o substituire e eliminat, cu excepția nodurilor text.

Deci mai trebuie să punem o regulă care să elimine și aceste noduri text. <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/comanda/produs"> <xsl:value-of select="self::node()[not(denumire) and not(@denumire)]|denumire|@denumire"/> <hr/> </xsl:template> <xsl:template match="text()"/> </xsl:stylesheet> A doua regulă se aplică tuturor nodurilor de tip text și, neconținând nimic, le substituie pe toate cu șirul vid, deci le șterge. Obs1. În atributele match ni se permite să nu mai punem cele 2 slashuri, deci match="text()" e echivalent cu match="//text()". Acest lucru e permis deoarece oricum valoarea lui match va fi considerată cale absolută de căutare. E o mică abatere de la sintaxa XPath standard, dar e suportată

Page 12: Seminar 8 UIA 2015

de XSLT datorită frecvenței cu care se fac match-uri prin căutare (fără să știm exact unde e elementul de care avem nevoie).

Generarea unei pagini Web complete Dacă percepem documentul XML ca o structură de date (sau chiar ca o bază de date), interogările XPAth joacă rolul lui SQL, iar transformările XSLT joacă rolul unui generator de rapoarte. "Rapoartele" finale sunt fie pagini Web (caz în care XSLT joacă rolul PHPului), fie bucăți de pagini Web (tabele, structuri de div-uri) ce urmează să fie inserate într-o pagină mai mare cu metode Ajax. Modificăm în continuare exemplul pentru a genera din codul XML o pagină HTML mai cuprinzătoare, care să conțină:

• Un titlu; • Un tabel cu 2 coloane: denumirile și prețurile; • Dedesubt, o celulă mare (COLSPAN) cu suma prețurilor.

Am amintit la un moment dat că un document XML nu ar trebui să conțină noduri calculate (de ex. totaluri) decât dacă e într-un stadiu final, gata de listare/afișare pentru userul final. Nodurile calculate de obicei se generează la momentul producerii formei finale a documentului (ca la generarea de rapoarte pentru baze de date). În cazul de față am putea prelua totalul direct din documentul original. Vom presupune totuși că el nu există acolo și îl vom calcula în cadrul transformării. <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <html> <head> <style> #cap {color:red} </style> </head> <body> <h1>Lista produselor din comanda</h1> <table border="1"> <tr id="cap"><td>Denumire</td><td>Pret</td></tr> <xsl:for-each select="comanda/produs"> <tr> <td> <xsl:value-of select="self::node()[not(denumire) and not(@denumire)]|denumire|@denumire"/> </td> <td> <xsl:value-of select="pret|@pret"/> </td> </tr> </xsl:for-each> <tr> <td colspan="2" align="right">

Page 13: Seminar 8 UIA 2015

<xsl:value-of select="sum(//pret|//@pret)"/> </td> </tr> </table> </body> </html> </xsl:template> </xsl:stylesheet> Observați că s-a generat o pagină Web completă, cu tot cu CSS (ar putea conține chiar și JavaScript!) Din acest exemplu reiese posibilitatea de a folosi XSLT ca generator dinamic de pagini Web, asemănător cu rolul pe care îl are PHP!

Regula de conservare S-a văzut la început că XSLT nu conservă implicit codul XML original. Dacă dorim să se conserve anumite elemente, trebuie să creăm o regulă de conservare care să substituie elementele cu ele însele:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet>

Această regulă substituie elementul rădăcină cu el însuși, producând o copie a documentului original (pentru execuție, modificați scenariul de transformare să nu se mai returneze html în browser). <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:strip-space elements="*"/> <xsl:template match="/"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet> Această regulă produce tot o copie a întregului document, dar elimină nodurile invizibile. <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/comanda/produs"> <xsl:copy-of select="."/> </xsl:template>

Page 14: Seminar 8 UIA 2015

</xsl:stylesheet> Această regulă conservă toate elementele produs. Pe lângă ele, datorită regulii implicite, se conservă și nodurile text din afara produselor. Studiați diferența față de următorul exemplu: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/comanda/produs"> <xsl:copy/> </xsl:template> </xsl:stylesheet> În acest caz, am folosit xsl:copy în loc de xsl:copy-of. Diferența e că se conservă produsele dar FĂRĂ conținut și atribute! Această tehnică se folosește când vrem să conservăm elemente existente dar să le redefinim conținutul și atributele: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/comanda/produs"> <xsl:copy> <xsl:attribute name="NouAtribut">NouaValoare</xsl:attribute>

<NouFiu>Nou continut</NouFiu> </xsl:copy>

</xsl:template> </xsl:stylesheet>

Nu e obligatoriu să folosim xsl:copy pentru conservare. Putem pur și simplu să retastăm elementul original, cu modificările dorite aplicate asupra lui: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/comanda/produs"> <produs PretNou="{@pret|pret}"> <NouFiu>Nou continut</NouFiu> </produs> </xsl:template> </xsl:stylesheet> Observați cum am creat un atribut PretNou care își ia valoarea fie din atributul vechi pret, fie din elementul pret (depinde pe care-l gasește).

Page 15: Seminar 8 UIA 2015

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/comanda/produs"> <xsl:copy> <xsl:value-of select="@pret|pret"/> </xsl:copy> </xsl:template> </xsl:stylesheet> În acest exemplu, conținutul produselor e redefinit să fie egal cu prețul (luat fie din atributul pret, fie din elementul-fiu pret, de la caz la caz). În următorul caz conservăm produsele, dar și conținutul lor textual. <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/comanda/produs"> <xsl:copy> <xsl:apply-templates/> </xsl:copy> </xsl:template> </xsl:stylesheet> Instrucțiunea xsl:apply-templates se utilizează de obicei împreună cu xsl:copy și are rolul ca, după ce s-a făcut copia goală a elementului, să o umple cu rezultatul eventualelor reguli care mai există pentru fiii elementului. În cazul de față, neexistând alte reguli, se aplică regula implicită de conservare a nodurilor de tip text, deci obținem produsele fără atribute dar cu conținutul textual. Evident, avem și posibilitatea de a defini reguli proprii care să fie invocate de xsl:apply-templates: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/comanda/produs"> <xsl:copy> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:template match="produs/*|produs/text()[normalize-space(.)!='']"> Aici a fost candva un fiu (vizibil) </xsl:template>

Page 16: Seminar 8 UIA 2015

</xsl:stylesheet> De data aceasta, xsl:apply-templates caută dacă există reguli pentru fiii produselor și, când o găsește pe a doua (aplicabilă fiilor de tip element și fiilor de tip text vizibil), o aplică. Conform acesteia, toți fiii din produs care sunt ori element, ori nod text vizibil, vor fi înlocuiți cu textul din interiorul regulii. <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/comanda"> <xsl:copy> <xsl:attribute name="client">Pop Ion</xsl:attribute> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:template match="produs"> <xsl:copy> <xsl:attribute name="PretNou"> <xsl:value-of select="@pret|pret"/> </xsl:attribute> <ID><xsl:value-of select="@id|id"/></ID> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:template match="text()[normalize-space(.)!='']"/> </xsl:stylesheet> Prima regulă:

• copiază o versiune goală a rădăcinii comanda, • îi adaugă atributul client • și o umple cu rezultatul eventualelor reguli care există pentru fiii săi.

Astfel se ajunge la a doua regulă care:

• realizează versiuni goale ale produselor, • le creează atributul PretNou cu valoarea luată din prețurile existente (fie din atributul vechi

pret, fie din elementul pret) • le adaugă subelemente ID cu valoarea luată din id-urile vechi (atribute sau elemente) • și mai adaugă rezultatul eventualelor reguli care mai există pentru fiii produselor.

A treia regulă caută noduri text vizibile și le șterge. Ea e apelată atât de prima regulă (ștergând nodurile 600 și "Aceasta este o comanda" care s-ar crea datorită regulii implicite de conservare a textului) cât și de a doua regulă (ștergând textul vechi din produse, care s-ar conserva datorită aceleiași reguli implicite).

Page 17: Seminar 8 UIA 2015

Concluzie: Rețineți regulile implicite din XSLT: • Codul documentului original nu e conservat în mod implicit, necesită o regulă de conservare

(totuși, se conservă nodurile text în mod implicit, și acestea au nevoie adesea de reguli care să le șteargă);

• Toate nodurile din documentul original care nu intră sub incidența nici unei reguli de substituire se elimină, cu excepția nodurilor text care se conservă (și concatenează între ele);

• Dacă un același nod e afectat de mai multe reguli de substituire, i se aplică regula cu match-ul cel mai specific (ex: match="comanda/produs" va avea prioritate față de match="produs", care e mai specific decât match="node()").

Cum putem rula transformări XSLT fără Oxygen?

1. XSLT 1.0 e suportat de orice browser. Foaia de transformare se atașează documentului XML original într-o manieră asemănătoare cu modul de atașare a unei foi de stiluri CSS, apoi pur și simplu se deschide documentul original în browser și el va apare gata transformat. 2. În context Ajax, putem aplica XSLT din JavaScript asupra răspunsului XML venit de la server, pentru a produce o bucată de cod HTML inserabilă în pagină.

• Procedura implică browser sniffing căci are sintaxă diferită de la un browser la altul; • Putem folosi librăria Sarissa care maschează diferențele dintre browsere; • Transformarea XSLT executată în JavaScript (deci în browser) poate substitui operațiile

clasice de manipulare a paginii prin JavaScript. 3. Putem aplica XSLT și pe server, inclusiv în PHP, astfel încât să livrăm spre JavaScript un răspuns gata transformat, gata de inserat (de ex. cu Ajax.Updater).

• Avantaj: nu mai trebuie transferată și foaia XSLT de la PHP la JavaScript; • Dezavantaj: e o abatere de la principiul Ajax ca sarcinile să se transfere pe cât posibil spre

browser, minimizând efortul serverului; în plus, dacă JavaScript controlează transformarea, poate să folosească datele originale și la altceva.

• Transformarea XSLT executată în PHP e frecvent folosită când codul XML a sosit în PHP din surse externe (de la un serviciu Web, alte servere, un depozit de documente) și dorim să folosim în site doar anumite informații din codul original (ca și în cazul JavaScript, XSLT devine o alternativă la operațiile clasice de manipulare DOM).

În practică, cel mai des întâlnite sunt transformările pe server, deoarece documentele XML circulă mai mult între servere decât între server și client (unde JSON tinde să fie preferat). În acest seminar exemplificăm doar prima variantă: Fișierul original va fi: <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="transf.xsl"?> <Comanda Client="PopIon"> <Produs ID="P1" Pret="100">Televizor</Produs> <Produs ID="P2" Pret="30">Ipod</Produs> </Comanda> A doua linie face legătura cu foaia de transformări. Dacă dorim ca, în loc de transformare, să aplicăm o foaie de stiluri CSS, înlocuim doar fișierul din HREF. Pentru a funcționa legătura, salvați foaia de transformări cu numele transf.xsl:

Page 18: Seminar 8 UIA 2015

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <html> <body> <h1>Comanda lui <xsl:value-of select="Comanda/@Client"/></h1> <table border="1"> <tr> <td>Denumire</td> <td>Pret</td> </tr> <xsl:for-each select="Comanda/Produs"> <tr> <td><xsl:value-of select="."/></td> <td><xsl:value-of select="@Pret"/></td> </tr> </xsl:for-each> <tr> <td colspan="2" align="left"> Total: <xsl:value-of select="sum(Comanda/Produs/@Pret)"/> </td> </tr> </table> </body> </html> </xsl:template> </xsl:stylesheet>

Din acest moment, dacă deschideți fișierul XML în browser, veți vedea pagina HTML rezultată din transformarea sa.