Tomasz Przechlewski Spis treści 1. Struktura arkusza XSLT... 1 2. Działanie arkusza XSLT... 2 3. Polecenia języka XSLT... 3 4. Zmienne... 5 5. Tworzenie dokumentu wynikowego... 5 6. Wbudowane funkcje... 6 7. Przykłady... 7 8. Dokumentacja/oprogramowanie... 9 1. Struktura arkusza XSLT XSLT (XSL Transformations) to język służący do transformacji XML-XML. Procesor XSLT zamienia wejściowy dokument XML (source tree) na podstawie arkusza XSLT tworząc wynikowy dokument XML (result tree). Arkusz (stylesheet) składa się z szablonów (templates). Każdy szablon opisuje jak należy przekształcić fragment dokumentu wejściowego na fragment dokumentu wyjściowego. Wykonanie transformacji polega na wywołaniu (instantiate) szablonu pasującego (match) do elementu głównego. Cały arkusz zawarty jest wewnątrz elementu stylesheet; elementami-dziećmi stylesheet mogą być wyłącznie tzw. polecenia najwyższego poziomu (top-level): template, output, include, import oraz param i variable. <?xml version="1.0" encoding="iso-8859-2"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/xsl/transform" version="1.0"> <xsl:import href="tpext.xsl" /> <!-- niższy priorytet --> <xsl:include href="tpdef.xsl" /> <!-- top level elements: --> <xsl:output method = { "xml" "html" "text" "QName" } encoding = "iso-8859-2" doctype-public="-//w3c//dtd HTML 4.01 Transitional//EN" doctype-system="..." indent={"yes" "no"} saxon:character-representation="native:decimal" xmlns:saxon="http://icl.com/saxon" /> <xsl:template name="qname" match="pattern" mode="qname" priority="number"> 1
<xsl:param>*... <xsl:variable name="qname" select="expression">... </xsl:variable> <xsl:param name="qname" select="expression">... </xsl:param> <xsl:key name="qname" match="pattern" use="expression" /> W powyższym zapisie QName oznacza qualified name (nazwę kwalifikowaną, tj. nazwa lokalna poprzedzona prefiksem zakończonym dwukropkiem). Zamiast stylesheet można użyć elementu transform. Treść szablonu jest wyrażeniem opisującym zawartość dokumentu wynikowego. Wynikiem transformacji jest dokument XML. Standard (XSLT 1.0) określa trzy sposoby serializacji (zapisania jako tekstu) wynikowych dokumentów: xml, text, html. Wszystkie pozostałe polecenia XSLT (XSLT instructions) muszą być zawarte wewnątrz elementów top-level. Tryby przetwarzania (modes) pozwalają na przetwarzanie tego samego węzła wielokrotnie. Podczas przetwarzania zawsze obowiązuje jeden tryb bieżący, który może się zmieniać przy wywołaniach apply-templates. Por. przykład poniżej. Szablony nazwane. Szablon może posiadać nazwę podaną jako wartość atrybutu name. Taki szablon można wywołać za pomocą instrukcji call-template (pełna analogia do deklarowania/uruchamiania funkcji w językach proceduralnych). Szablon musi posiadać co najmniej jeden z atrybutów match lub/i name, może posiadać oba. Polecenia include i import różnią się priorytetem: deklaracje oraz szablony arkusza importującego mają piorytet nad deklaracjami/szablonami arkusza importowanego. 2. Działanie arkusza XSLT Wywoływanie szablonów może być wykonywane na dwa sposoby: <!-- XSLT instructions: --> <xsl:apply-templates select="expression" mode="qname" > ( <xsl:with-param> <xsl:sort> ) * </xsl:apply-templates> <xsl:call-template name="qname" > <xsl:with-param> * </xsl:call-template> <xsl:with-param name="qname" select="expression">... </xsl:with-param> Polecenie <apply-templates select="expression"/> powoduje obliczenie wartości wyrażenie, przy czym wynikiem musi być zbiór węzłów (node-set). Dla każdego węzła ze zbioru następuje: 1. dopasowanie szablonu najlepiej pasującego do wę- 2
zła. 2. wykonanie dopasowanego szablonu z węzłem ze zbioru-węzłów jako węzłem bieżącym. 3. wstawienie do wynikowego drzewa wynikowego (aka dokumentu wynikowego). Instrukcja <call-template name="nazwa"/>, umożliwia wywołanie szablonów nazwanych (named templates). W tym przypadku węzeł bieżący się nie zmienia. Atrybut match w elemencie template określa do jakich węzłów szablon zostanie zastosowany. Jego wartością musi być wzorzec (pattern). Wzorzec jest podzbiorem wyrażenia ścieżkowego języka XPath, tj. każdy wzorzec jest poprawnym wyrażeniem XPath ale odwrotnie nie jest prawdziwe. Kiedy wykonywany jest szablon dla węzła, to węzeł staje się węzłem bieżącym (current node). Instrukcje, które zmieniają węzeł bieżący to: apply-templates i foreach. W wyrażeniach XPath funkcja current() zwraca węzeł bieżący. W wyrażeniach XPath wyrażenia ścieżkowe są obliczane względem węzła kontekstowego (context node). Mówiąc precyzyjniej: każdy krok wyrażenia ścieżkowego wyznacza zbiór-węzłów względem węzła kontekstowego. Każdy węzeł w tym zbiorze jest węzłem kontekstowym dla wyznaczenia zbioru węzłów w następnym kroku. Innymi słowy węzeł kontekstowy jest równy węzłowi bieżącemu na początku obliczania wyrażenia ścieżkowego XPath, a dalej -- w miarę przechodzenia kolejnych kroków ścieżki XPath -- zmienia się. Węzeł kontekstowy jest oznaczany kropką. (co jest skrótem od self::node()) 3. Polecenia języka XSLT Do tworzenia poszczególnych węzłów dokumentu wynikowego służą odpowiednie następujące szablonu: <xsl:value-of select="expression" /> <xsl:attribute name="qname">...</xsl:attribute> <xsl:comment>... </xsl:comment> <xsl:element name="qname">... </xsl:element> <xsl:text>... </xsl:text> <xsl:copy>... </xsl:copy> <xsl:copy-of select='expression'>... </xsl:copy-of> Polecenie value-of oblicza wartość wyrażenia a następnie konwertuje tą wartość do napisu i wypisuje do drzewa wynikowego (jako węzeł tekstowy). Zasady konwersji są identyczne jak zaimplementowane w funkcji string() języka XPath. Jeżeli obliczoną wartością jest zbiór węzłów, to wszystkie węzły za wyjątkiem pierwszego (w porządku dokumentu) są ignorowane. Dla pliku z przykładu Sekcja 7, Przykłady : 3
<value-of select='//name' /> <!-- wypisze tylko pierwszą nazwę --> <value-of select='/zestawienie/@rok' /> <!-- wypisze 2003 --> Atrybuty elementów XSLT, których wartości są wyrażeniami XPath zaznaczono podając jako wartość atrybutu słowo Expression. W przypadku pozostałych atrybutów można wstawić wyrażenie XPath używając notacji {Expression} (nazywa się to attribute value template). Takie wyrażenie jest obliczane w wynik zamieniany na napis i wstawiany do drzewa wynikowego. (Por. Sekcja 7, Przykłady ): <template match='czesc'> <img src='{@zdjecie}' alt='{nazwa}'/> </template> Polecenie copy kopiuje węzeł bieżący z drzewa źródłowego do drzewa wynikowego. Kopiowanie jest powierzchowne (shallow copy) tj. nie są kopiowane elementy potomne ani atrybuty; aby skopiować wszystkie węzły potomne należy albo użyć copy-of (kopiuje zbiór-węzłów) albo trzeba posłużyć się następujących szablonem: <xsl:template match='@* node()' mode='copy'/> <xsl:copy> <xsl:apply-templates select='@*' mode='copy'/> <xsl:apply-templates mode='copy'/> </xsl:copy> <xsl:template> W szczególności powyższy przykład pozwala zgrabie rozwiązać problem pn. skopiuj cały dokument XML za wyjątkiem... Polecenia sterujące (if, choose, for-each). Pętla for-each iteruje po zbiorze węzłów określonym za pomocą atrybutu select. W każej iteracji kolejny węzeł ze zbioru staje się węzłem bieżącym. Jeśli pierwszymi podelementami for-each będą sort, elementy zbioru-węzłów zostaną posortowane. <xsl:if test="expression">... </xsl:if> <xsl:choose> <xsl:when test='expression'>+ <xsl:otherwise>? </xsl:choose> <xsl:for-each select="expression"> <xsl:sort select='expression'... >* </xsl:for-each> <xsl:sort select="expression" order= {"ascending" "descending" } data-type={ "text" "number" "QName" } /> 4
4. Zmienne Zmienne w XSLT są deklaratywne (jak w programowaniu funkcyjnym): po przypisaniu wartości zmiennym nie mogą być one zmienione. W rezultacie zmienne w XSLT przypominają bardziej stałe z proceduralnych języków programowania. Zmienne zadeklarowane za pomocą variable/param mogą być wykorzystanie w wyrażeniach XPath; należy nazwę zmiennej poprzedzić znakiem $, np. $czesci. Deklaracja zmiennej lokalnej może wystąpić wewnątrz szablonów (wszędzie tam, gdzie mogą występować inne instrukcje XSLT). Zmienna jest widoczna do końca elementu, w którym została zadeklarowana. Zmienne globalne deklarowane są jako polecenia top-level. Zmienna globalna jest widoczna we wszystkich szablonach oraz deklaracjach innych zmiennych globalnych. Wartość zmiennej i/lub parametru można podać na dwa sposoby: 1. poprzez wyrażenie XPath w atrybucie select, wówczas przypisywana jest obliczona wartość wyrażenia; 2. poprzez zawartość elementu variable, która jest interpretowana tak jak fragment szablonu; wynik tej interpretacji jest przypisywany jako wartość zmiennej (jako węzeł z ewentualnym węzłami potomnymi). W XSLT 1.0 istnieje różnica między wartością wyrażenia z atrybutu select a wartością uzyskaną w wyniku interpretacji zawartości variable. Ta druga jest typu result tree fragment i nie można już na niej wykonywać takich operacji jak for-each czy apply-templates. To rozróżnienie i ten typ danych zniknął już w wersji 1.1 XSLT jako niepotrzebna komplikacja. Parametry arkusza (globalne) są zadeklarowane w elementach param na głównym poziomie arkusza. Elementy param umieszczonych na początku szablonu są parametrami szablonu. Do przekazania wartości parametrów do szablonu służą elementy with-param umieszczone wewnątrz apply-templates lub call-template. Wartość określa się tak samo jak wartość zmiennych lub domyślną wartość parametrów. Szablony wraz z parametrami są odpowiednikiem funkcji w proceduralnych językach programowania. 5. Tworzenie dokumentu wynikowego Polecenie document tworzy nowe drzewo wynikowe, które zostanie zapisane do odrębnego pliku. Pozwala to na zapisanie dokumentu wynikowego do wielu plików. Polecenie document zostało wprowadzone w wersji 1.1 standardu XSLT. <xsl:document href='uri' <!-- nazwa pliku --> method='xml html text Qname' <!-- znaczenie jak w elemencie output --> encoding='string' <!-- kodowanie pliku --> doctype-public = "..." doctype-system = "..."/> Przy serializacji XML i XHTML procesor XSLT zapisuje czasami do dokumentu wynikowego zbędne deklaracje przestrzeni nazw. Aby tego uniknąć, można wykorzystać atrybut exclude-result-prefixes. W atrybucie tym można umieścić: listę rozdzielonych spacjami prefiksów (związanych z przestrzeniami nazw, które mają być pominięte), dodatkowo na liście może znajdować się napis #default odnoszący się do 5
domyślnej w danym miejscu przestrzeni nazw. Atrybut exclude-result-prefixes może wystąpić: -- w każdym elemencie XSLT, bez prefiksu xsl, -- w każdym elemencie wynikowym, z prefiksem xsl (lub innym wskazującym na przestrzeń nazw XSLT). Wyłączenie przestrzeni nazw obowiązuje w całym poddrzewie elementu w którym występuje atrybut exclude-result-prefixes. To rozwiązanie nie gwarantuje, że deklaracja przestrzeni nazw nie pojawi się w wyniku. W szczególności deklaracja pojawi się, jeżeli jakiś element lub atrybut wyniku należy do danej przestrzeni nazw. 6. Wbudowane funkcje XSLT rozszerza o kilka nowych zestaw funkcji dostępnych w wyrażeniach XPath: current() zwraca węzeł bieżący (opisane wyżej). document(uri), document(uri, base-uri) Dokument wskazany przez URI jest parsowany; zwracany jest węzeł-korzeń (root node) tego dokumentu. Argument URI nie musi być napisem, może być zbiorem węzłów (w tym przypadku nastąpi konwersja, szczegółowo opisana w specyfikacji). Gdy zbiór węzłów sprowadza się do pojedynczego węzła, np.: document (@href), to działanie funkcji polega na zwróceniu węzła głównego dokumentu określonego przez wartość atrybutu @href. Jeżeli URI zawiera adres względny to jest on interpretowany względem dokumentu zawierającego go (tzn. jeżeli URI jest w arkuszu to wzlędem arkusza; jeżeli jest w dokumencie źródłowym, to względem dokumentu źródłowego). Drugi argument funkcji pozwala zmienić domyślny adres bazowy. Ten drugi argument także może być zbiorem węzłów. Jeżeli tak jest, to adresem bazowym będzie URI dokumentu, z którego wybrano zbiór węzłów. Przykład: document("@href", "/") -- jako adres bazowy przyjmie URI dokumentu źródłowego (bo / oznacza korzeń dokumentu źródłowego dokumentu). Często używany zapis document() oznacza URI arkusza XSLT. generate-id(node-set?) zwraca napis jednoznacznie identyfikujący węzeł (mówiąc precyzyjniej zwraca xs:name). Jeżeli nie podano Node-set domyślną wartością jest węzeł kontekstowy. Funkcja umożliwia tworzenie wszelkiej maści odsyłaczy. Przykładowo załóżmy, że dokument XML zawiera elementy title zawierający tytuł punktu, wówczas: <xsl:template match='title' mod='toc'> <!-- formatuje tytuł punktu w spisie treści: --> <div class='toc.item' ><a href='{#generate-id()}'> <xsl-value-of select='.'/></a></div> <!--.... --> <xsl:template match='title' > <!-- formatuje tytuł punktu w treści dokumentu: --> <h3 id='{generate-id()}'> <xsl-value-of select='.'/> </h3> 6
Atrybut mode, co pozwala na dwukrotne przetworzenie dokumentu. Zwróć też uwagę na użycie notacji {... } (attribute value templates) 7. Przykłady Plik ramy.xml ma następującą prostą strukturę: <zestawienie rok="2003"> <czesc id="m1201" typ="rama" zdjecie='gx2_carbon.jpg'> <nazwa>gx2 Carbon</nazwa> <firma>merckx</firma> <cena>3300</cena> <sprzedaz>2</sprzedaz> </czesc> <czesc id="m2345" typ="rama" zdjecie='merckx_teamsc.jpg'> <nazwa>team SC</nazwa> <firma>merckx</firma> <cena>2150</cena> <sprzedaz>2</sprzedaz> </czesc>... </zestawienie> Bardzo prosty arkusz: wykorzystywane jest tylko polecenia xsl:template, xsl:value-of oraz xsl:apply-templates Wypisanie zawartosci pliku ramy.xml w postaci tabelki: <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/xsl/transform" version="1.0"> <xsl:output method="html" encoding="iso-8859-2" doctype-public="-//w3c//dtd HTML 4.01 Transitional//EN" indent="yes" /> <xsl:template match="/"> <html> <head> <title>przykład 1</title> </head> <body bgcolor="#ffffff"> <xsl:apply-templates/> </body> </html> <xsl:template match="zestawienie"> <table> <thead> <tr><td>id</td><td>nazwa</td><td>firma</td><td>usd</td></tr> </thead> 7
<tbody> <xsl:apply-templates/> </tbody> </table> <xsl:template match="czesc"> <tr> <td><xsl:value-of select="@id"/></td> <td><xsl:value-of select="nazwa"/></td> <td><xsl:value-of select="firma"/></td> <td><xsl:value-of select="cena"/></td> </tr> </xsl:stylesheet> Znajduje ramy droższe od 2000, wypisuje w porzadku od najdroższej do najtańszej: <?xml version="1.0" encoding="iso-8859-2"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/xsl/transform" version="1.0"> <xsl:output method="text" encoding="iso-8859-2" /> <xsl:template match="zestawienie"> <xsl:for-each select="//czesc[@typ='rama'][./cena > 2000]"> <xsl:sort select="./cena" data-type="number" order='descending'/> <xsl:value-of select="./nazwa"/> <xsl:text> </xsl:text> <xsl:value-of select="./firma"/> <xsl:text> : </xsl:text> <xsl:value-of select="./cena"/> <xsl:text> </xsl:text> </xsl:for-each> </xsl:stylesheet> Ile sprzedano ram firmy $firma?: <?xml version="1.0" encoding="iso-8859-2"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/xsl/transform" version="1.0"> <xsl:output method="text" encoding="iso-8859-2" /> <xsl:param name="firma" select="colnago"/> <!-- źle --> <xsl:variable name="obrot" select="sum(//czesc[@typ='rama'][./firma=$firma]/sprzedaz)" /> <xsl:template match='/'> <xsl:text>sprzedano: </xsl:text> <xsl:value-of select="$obrot" /> 8
<xsl:text> ram firmy: </xsl:text> <xsl:value-of select="$firma" /> <xsl:text> </xsl:text> </xsl:stylesheet> 8. Dokumentacja/oprogramowanie James Clark: XSL Transformations (XSLT), W3C Recommendation, 16 November 1999 http://www.w3.org/tr/xslt/ James Clark, Steve DeRose: XML Path Language (XPath), W3C Recommendation, 16 November 1999 http://www.w3.org/tr/xpath/ Michael Kay: SAXON The XSLT and XQuery Processor http:// saxon.sourceforge.net/. Daniel Veillard: The XML C parser and toolkit of Gnome http://xmlsoft.org/. Ten dokument w formacie: pdf [./xml-xslt.pdf] oraz xml [./xml-xslt.xml]. 9