Kurs XML
Przekształcanie i oglądanie danych XML
Język XSL (który sam jest aplikacją XML) pozwala
przekształcać dane XML na podstawie czegoś w rodzaju szablonu.
Korzystając z tego narzędzia możesz dane przegrupowywać i
wybierać. Możesz je także przygotować do wizualnego
przeglądania.
Arkusze XSLT, czy może XSL?
Na standard XSL składa się język samych
przekształceń (XSLT) i język opisu graficznego. Ponieważ opis
graficzny XSL nie jest przez żadną z powszechnie używanych
przeglądarek internetowych obsługiwany, do prezentacji danych
używa się HTML'a, lub jego młodszego brata - XHTML'a. Cała
rzecz opiera się na automatycznym poumieszczaniu danych
pobranych z dokumentu XML, w wynikowym pliku HTML. Taki plik HTML
służy do prezentacji wybranych w trakcie przekształceń XSLT
danych. Ta technika nadaje się wyśmienicie do prezentowania
danych w Internecie. Z jednej strony umożliwiamy programom
pobranie samych danych, z drugiej serwujemy czytelnikowi ładnie
graficznie przygotowane dane. Co więcej, dane te można także
zmusić do interakcji z użytkownikiem. Cały czas możemy
przecież mieć do nich dostęp.
Arkusz i dane
Arkusze stylów XSL mogą być osobnymi plikami, które
za pomocą odpowiedniej deklaracji w prologu przypisuje się
dokumentom z danymi. Oczywiście jednego arkusza stylów można
używać do przekształceń wielu plików z danymi tworzącymi
tę samą strukturę. Tak naprawdę, korzystając z tego, że
arkusze stylów są zorientowane na elementy i inne węzły
dokumentu XML, można wybiórczo przekształcać poszczególne
węzły z danymi. Oczywiście najczęściej jest nim po
prostu korzeń dokumentu.
Ważne jest, aby korzeń nie został pomylony z
głównym elementem. Korzeń zawiera cały dokument. Nie tylko
element główny, ale także prolog. W hierarchii znajduje się
więc najwyżej.
Oto, jak powinna wyglądać deklaracja dołączająca
arkusz XSL. Umieszczana oczywiście w prologu dokumentu z danymi:
<?xml-stylesheet type="text/xml" href="default.xsl"?>
Gdzie default.xsl jest nazwą pliku z arkuszem
XSL.
To jest najprostszy sposób powiązania XML z XSL.
Jeśli otworzymy plik XML z taką deklaracją i oczywiście
danymi, to przeglądarka wczyta arkusz stylów, przetworzy dane a
efekty wyświetli tak samo, jak wyświetla pliki HTML.
Jeżeli używasz przeglądarki Internet Explorer
5, czyli jedynej, która potrafi korzystać z XSL, powinieneś
zamienić w powyższej deklaracji wartość parametru type
z text/xml na text/xsl. W przeciwnym wypadku nie
będzie ona wiedziała, że chodzi o arkusz XSL. Wersja 6.0
powinna już zachowywać się poprawnie, bez takich zachcianek.
Niestety, z Internet Explorerem 5 jest jeszcze trochę
podobnych, jak opisany w ramce, problemów. W związku z tym
nęcącą alternatywą jest użycie jednego z samodzielnych
procesorów XSL, które podobnych komplikacji nie stwarzają.
Oczywiście traci się wtedy sporo, bo uzyskiwany jest zwykły
dokument HTML. Na razie zapraszam do korzystania z Internet
Explorera i dołączanych, zewnętrznych arkuszy stylów, możliwości
wcale nie są małe.
Zupełnie nieciekawe uwagi: a co na to wszystko style CSS?
Znane z języka HTML Kaskadowe Arkusze Stylów (CSS)
mogą być wykorzystywane także razem z XML'em. Arkusze CSS
składają się ze zbioru reguł opisujących wygląd poszczególnych
elementów w przeglądarce. I to cała filozofia CSS. Nie ma
więc tu mowy o wybieraniu poszczególnych danych, czy choćby o
wyświetlaniu wartości atrybutów. W zamian, otrzymujesz możliwość
oglądania twoich dokumentów zarówno w przeglądarce Internet
Explorer 5, jak i Netscape 6.
Jeżeli jesteś zainteresowany użyciem CSS, możesz
skorzystać z dostępnych w polskim Internecie bardzo dobrych
materiałów. Ich spis znajdziesz w rozdziale Zasoby. Teraz
zaprezentuję tylko przykładowe połączenie arkusza CSS i
dokumentu XML, wraz z wynikiem w przeglądarce.
<?xml-stylesheet type="text/css" href="arkusz.css"?>
<dokument>
<h>Zaawansowane aspekty optymalizacji pracy</h>
<par>W tym rozdziale postaram się opisać, jak podłączyć mysz do portu
klawiatury.</par>
</dokument>
Zwróć uwagę na pierwszy wiersz. Zawiera on
deklarację dołączająca arkusz CSS. Różnica między
deklaracją arkusza XSL, polega na różnej wartości parametru
type.
Oto arkusz CSS zawierający zbiory reguł dla trzech
elementów dokumentu:
dokument {
font-family: "MS Sans Serif", sans-serif;
}
h {
display: block;
font-size: 16pt;
font-weight: bold;
padding: 10px;
}
par {
display: block;
font-size: 10pt;
padding: 10px;
}
Tak to razem wygląda w przeglądarce:
![[9274 bajtów]](xml/cssns.gif) |
Przeglądarka Netscape/Mozilla 6 i plik
produktywnosc.xml |
Wróćmy jednak do XSL. Może jest trochę bardziej
skomplikowany, ale ma znacznie większe możliwości.
Struktura arkusza XSL
Główny element każdego arkusza XSL to xsl:stylesheet.
Jego podstawowym, wymaganym w zależności od parsera atrybutem
jest version - aktualnie 1.0. Ma on także atrybut
xmlns:xsl, który odpowiada za lokalizację dokumentu z
przestrzenią nazw dla XSLT. Co to takiego przestrzeń nazw,
dowiesz się w następnym rozdziale. Ważne jest teraz, że w
związku z koniecznością identyfikacji poleceń XSLT, muszą
być one poprzedzane jakimś przedrostkiem. Najczęściej jest
nim po prostu xsl:.
Arkusz XSL musi zawierać reguły, które będą
pasowały do węzłów elementu źródłowego. Musisz zawsze
pamiętać, że dokument źródłowy jest traktowany jako drzewo
elementów, atrybutów i innych węzłów. Ale skupmy się na
samym arkuszu. Wewnątrz jego głównego elementu xsl:stylesheet
mogą znajdować się podelementy - sekcje xsl:template.
Wybierają one określone przez atrybut match węzły
przetwarzanego dokumentu źródłowego. Z wyselekcjonowanych w
ten sposób węzłów już można pobrać (i wstawić do wyniku)
dane, można je także skierować do dalszego przetwarzania,
poprzez umieszczenie dyrektywy xsl:apply-templates. Po jej
spotkaniu parser zatrzyma obsługę bieżącej sekcji xsl:template
i zacznie szukać nowej, odpowiadającej węzłom-dzieciom
bieżącego taga. Kiedy wsztkie węzły-dzieci już przetworzy,
to wróci do pierwotnej sekcji xsl:template. Oczywiście
węzły-dzieci mogą zawierać własne węzły-dzieci, które
również mają własne sekcje xsl:template. Oto
przykładowy, bardzo prosty kod:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<xsl:apply-templates/>
</html>
</xsl:template>
</xsl:stylesheet>
Jeśli w sekcji xsl:template nie
zastosujesz polecenia xsl:apply-templates, to żadne nie
przekształcone dotychczas podelementy bieżącego elementu nie
zostaną przekształcone, nawet jeśli są odpowiadające im inne
sekcje xsl:template.
Ten dokument zawiera dokładnie jedną sekcję xsl:template.
Wartością jej parametru match jest /, co oznacza
korzeń dokumentu (tak, korzeń też jest węzłem). Oczywiście
procesor XSL zawsze przejdzie do takiej sekcji xsl:stylesheet
i to na samym początku przetwarzania każdego dokumentu XML.
Jest tak dlatego, że w strukturze dokumentu XML zawsze najwyżej
znajduje się korzeń. Jak widać, wprawdzie w tej sekcji xsl:template
jest instrukcja xsl:apply-templates, ale mogło by jej tam
w ogóle nie być. Dlaczego? Procesor i tak nie znajdzie innych
sekcji xsl:template, bo ich po prostu nie ma. Jeśli
użyjemy takiego prostego arkusza stylów, to wynikem przekształceń
absolutnie każdego poprawnie sformatowanego dokumentu XML
będzie:
<html>
</html>
Jak widać, tagi nie poprzedzone prefiksem xsl:,
zostaną po prostu umieszczone w dokumencie wynikowym. Ważne
jest, że nawet jeśli przeprowadzasz tranformacje do HTML,
musisz pamiętać o zachowaniu składni XML (w uproszczeniu
można więc przyjąć, że zamiast HTML'a będziesz używać
XHTML'a).
Jeśli do przekształceń chcesz użyć Internet
Explorera 5 (plik XML chcesz wyświetlać bezpośrednio), to
zamień wartość parametru xmlns:xsl na http://www.w3.org/TR/WD-xsl.
W przeciwnym razie przeglądarka nie zrozumie poleceń XSL.
Internet Explorer 6 ma już zachowywać się poprawnie.
Spróbujmy teraz przeprowadzić transformację
konkretnego dokumentu:
<dokument tytuł="Zaawansowane aspekty optymalizacji pracy">
<par>W tym rozdziale postaram się opisać, jak podłączyć mysz do
portu klawiatury.</par>
<par>Myślę, że warto to przeczytać.</par>
</dokument>
Występują w nim następujące węzły: korzeń,
element dokument, jego parametr tytuł oraz dwa
elementy par. Wszystkie interesujące nas dane należą do
elementu dokument, więc to on będzie pierwszym celem
instrukcji xsl:template:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="dokument">
<html>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Jak widzisz, w atrybucie match umieściłem po
prostu nazwę szukanego elemnetu dokument. Po jego
odnalezieniu do wyniku zostanie wstawione trochę kodu HTML (dwa
otwierające znaczniki), następnie procesor XSL wykona
instrukcję xsl:apply-templates, czyli będzie próbował
przetwarzać elementy potomne i na koniec ponownie wstawi trochę
kodu HTML (tym razem dwa zamykające znaczniki).
Teraz zajmiemy się wyciągnięciem z dokumentu
wszelkich możliwych danych. Za pobieranie rozmaitych wartości
odpowiada instrukcja xsl:value-of, która posiada jeden
parametr - select. Określa on, co ma zostać pobrane.
Bieżący węzeł oznacza tutaj .. Aby wstawić zawartość
jakiegoś podelementu, po prostu wpisz do parametru select
jego nazwę. Jeśli chcesz wstawić wartość atrybutu, podaj
jego nazwę, poprzedzoną znakiem małpy. Kiedy ścieżka
zawiera więcej niż jeden element (czy np. atrybut i
podelement), oddzielaj wszystko znakami /.
W przypadku pobierania wartości bieżącego
węzła, w zależności od specyfikacji oraz procesora XSL,
będziesz mógł parametr select pominąć. Nie jest to
jednak zalecane, bo ogranicza przenośność twojego kodu.
Oto uzupełniony arkusz XSL. Zwróć uwagę na
instrukcje xsl:value-of:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="dokument">
<html>
<head>
<title> <xsl:value-of select="@tytuł"/> </title>
</head>
<body>
<h1> <xsl:value-of select="@tytuł"/> </h1>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="par">
<p> <xsl:value-of select="."/> </p>
</xsl:template>
</xsl:stylesheet>
Oczywiście po odczytaniu polecenia xsl:apply-templates,
procesor XSL przejdzie do pierwszej odpowiadającej elementowi
par (bo to kolejny element w dokumencie źródłowym)
sekcji xsl:template (tutaj: druga sekcja tego typu). Oto
kod wynikowy:
<html>
<head>
<title>Zaawansowane aspekty optymalizacji pracy</title>
</head>
<body>
<h1>Zaawansowane aspekty optymalizacji pracy</h1>
<p>W tym rozdziale postaram się opisać, jak podłączyć mysz do
portu klawiatury.</p>
<p>Myślę, że warto to przeczytać.</p>
</body>
</html>
w przeglądarce:
![[9940 bajtów]](xml/xslie.gif) |
Przeglądarka Internet Explorer i wczytany z
pliku HTML wynik przekształceń |
Tyle wystarczy, jeśli przekształcasz dokument za
pomocą jednego z zewnętrznych, zgodnych ze specyfikacją parserów.
Jeśli zaś korzystasz z przekształceń w locie Internet
Explorera 5 i być może jeszcze innych, bardziej wymagających
procesorów, to zawsze musisz uwzględnić korzeń dokumentu.
Więc aby arkusz działał także z tą przeglądarką,
powinieneś dodać na samym jego początku sekcję xsl:template
z atrybutem match równym / (jak pamiętasz, oznacza
to korzeń), która zawiera instrukcję xsl:apply-templates.
Oto ostateczny, bardziej uniwersalny arkusz:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="dokument">
<html>
<head>
<title> <xsl:value-of select="@tytuł"/> </title>
</head>
<body>
<h1> <xsl:value-of select="@tytuł"/> </h1>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="par">
<p> <xsl:value-of select="."/> </p>
</xsl:template>
</xsl:stylesheet>
Warto w tym miejscu przypomnieć, że arkusz będzie
działał z przeglądarką Internet Explorer 5 tylko wtedy, gdy
zastosujesz odpowiednią wartość parametru xmlns:xsl,
wskazując na szkic roboczy, a nie wersję finalną XSLT (patrz:
druga ramka, w tym podrozdziale).
xsl:for-each i inne dziwactwa
Jeśli chcesz po kolei przekształcać wszystkie
elementy spełniające dany warunek, możesz użyć polecenia xsl:for-each.
Posiada ono atrybut select, który określa, co ma zostać
przekształcone. Dla przykładu:
<xsl:template match="dokument">
<xsl:for-each select="par">
<p> <xsl:value-of select="."/> </p>
</xsl:for-each>
</xsl:template>
Kod z wydruku powyżej pobierze po kolei wszystkie
zawartości znajdujących się wewnątrz elementu dokument
elementów par i umieści je między znacznikami p.
Podczas wyboru węzłów do przekształceń dostępne
są wieloznaczniki. Pozwalają one tworzyć reguły domyślne, których
stosowanie daje gwarancję, że żaden element z dokumentu źródłowego
nie zostanie pominięty. Bez znaczenia, jakie on zajmuje miejsce
w strukturze. Podstawowym symbolem wieloznacznym jest *.
Można go oczywiście użyć w atrybucie match sekcji xsl:template.
Jeśli chcesz aby reguła działała także dla korzenia, możesz
użyć symbolu /:
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
Podobne czary można wyczyniać dla węzłów z
samymi danymi - dla zawartości tekstowych należy zastosować
text(), a dla atrybutów * poprzedzoną znakiem małpy.
Razem daje to text()|@*. Jeśli rzecz dodatkowo uzupełnimy
o automatyczne wstawianie bieżących wartości, to otrzymamy:
<xsl:template match="text()|@*">
<xsl:value-of select="."/>
</xsl:template>
Jeszcze jeden fascynujący przykład!
Zbudujmy przykładowy arkusz pokazujący dane z
prezentowanego w drugim rozdziale pliku ludzie.xml:
<ludzie>
<człowiek dane="poprawne">
<imie>Paweł</imie>
<nazwisko>Stroiński</nazwisko>
<poczta>pawel@pabloware.w.pl</poczta>
<web>http://www.pabloware.w.pl/</web>
</człowiek>
<człowiek dane="fikcyjne">
<imie>Jan</imie>
<nazwisko>Kowalski</nazwisko>
<poczta>webmaster@kowalski.net</poczta>
<web>http://www.kowalski.net</web>
</człowiek>
</ludzie>
Tym razem zaczniemy od uwzględnienia korzenia, a w
obsłudze elementu ludzie dodamy trochę kodu HTML:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="ludzie">
<html>
<body>
<table border="1">
<tr>
<td><b>Dane</b></td><td><b>Imię</b></td><td><b>Nazwisko</b></td>
<td><b>Poczta</b></td><td><b>Web</b></td>
</tr>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Pora na właściwe dane - element człowiek.
Można zastosować proste przejście do obsługi elementów
potomnych (które i tak później trzeba obsłużyć):
<xsl:template match="człowiek">
<tr>
<xsl:apply-templates/>
</tr>
</xsl:template>
Ale nie ma takiej potrzeby. Wszystko można wykonać w
jednej sekcji, posługując się tylko instrukcją xsl:value-of.
Oto uzupełniony kod:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="ludzie">
<html>
<body>
<table border="1">
<tr>
<td><b>Dane</b></td><td><b>Imię</b></td><td><b>Nazwisko</b></td>
<td><b>Poczta</b></td><td><b>Web</b></td>
</tr>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="człowiek">
<tr>
<td><xsl:value-of select="@dane"/></td>
<td><xsl:value-of select="imie"/></td>
<td><xsl:value-of select="nazwisko"/></td>
<td><xsl:value-of select="poczta"/></td>
<td><xsl:value-of select="web"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
I wynik jego działania:
![[8140 bajtów]](xml/xslie2.gif) |
Przeglądarka Internet Explorer i plik
ludzie.xml |
Właściwie wszystko byłoby w porządku, gdyby nie
fakt, że adresy z dwóch ostatnich komórek nie są aktywne. Jak
je uaktywnić? Oczywiście umieszczając wewnątrz HTML'owego
odsyłacza - elementu A. Pojawia się jednak problem.
Jedyną instrukcją umożliwiającą pobranie danych z dokumentu
źródłowego jest xsl:value-of. Żeby odsyłacz działał,
musimy tę instrukcję wstawić w jego parametrze href
(cel). Ale jak? Przecież wewnątrz atrybutów nie można
umieszczać znaczników. Rozwiązaniem są zmienne (a często to
właściwie stałe) XSL, deklarowane poprzez xsl:variable:
<xsl:variable name="poczta.url">
mailto:<xsl:value-of select="poczta"/>
</xsl:variable>
Zmienne XSL nie są poprawnie interpretowane
przez przeglądarkę Internet Explorer 5. Na szczęście parser
MSXML4 (z IE6) już je bez problemów obsługuje. Pozostaje więc
używać jednego z autonomicznych procesorów XSL i czekać
na upowszechnienie się Internet Explorera 6.
Widoczny powyżej kod przypisuje zmiennej poczta.url
wartość podelementu o nazwie poczta, poprzedzoną
prefiksem mailto:. Aby teraz wstawić do atrybutu wartość
zmiennej poczta.url, należy użyć formuły {$poczta.url}.
Natomiast aby umieścić wartość zmiennej w tekstowej
zawartości znacznika, należy skorzystać ze zwykłego
polecenia xsl:value-of. W jego parametrze select
trzeba wpisać poprzedzoną znakiem dolara nazwę zmiennej. Oto
nowa sekcja obsługi elementu człowiek:
<xsl:template match="człowiek">
<tr>
<td><xsl:value-of select="@dane"/></td>
<td><xsl:value-of select="imie"/></td>
<td><xsl:value-of select="nazwisko"/></td>
<xsl:variable name="poczta">
<xsl:value-of select="poczta"/>
</xsl:variable>
<td><a href="mailto:{$poczta}"><xsl:value-of select="$poczta"/></a></td>
<xsl:variable name="web">
<xsl:value-of select="web"/>
</xsl:variable>
<td><a href="{$web}"><xsl:value-of select="$web"/></a></td>
</tr>
</xsl:template>
Wyjściem z sytuacji jest także użycie xsl:attribute.
Zmienne XSL są tu jednak o tyle lepsze, że pozwalają na
wielokrotne wstawienie wartości bez wielokrotnego jej
odczytywania. [Adres musi być tutaj wstawiany po dwa dwa razy
- raz do zawartości znacznika A i raz do jego paramtru
href.] O instrukcji xsl:attribute będzie mowa
jeszcze w tym podrozdziale.
Hm
, przydałoby się jeszcze wyróżnić jakoś
rekordy zawierające fikcyjne dane. Powiedzmy, zmienić dla nich
kolor tła na szary. Aby procesor XSL wiedział co jest rekordem
z fikcyjnymi danymi, musimy użyć instrukcji wyboru.
W XSL'u dostępne są dwie takie instrukcje. Pierwsza
- prostsza - to xsl:if. W jej atrybucie test
umieszcza się warunek, a wewnątrz kod wstawiony do wyniku w
przypadku, gdy warunek przeszedł test pomyślnie. Dostępna jest
także odrobinę bardziej skomplikowana instrukcja xsl:choose.
Pozwala ona dokonać wyboru spośród kilku możliwości (jej
podelementy xsl:when), a także wykonać działania, gdy
żaden warunek nie zostanie spełniony (podelement xsl:otherwise),
np.:
<xsl:choose>
<xsl:when test="xf/requiem722@scenariusz='rewelacyjny'">
cóż, można tak powiedzieć
</xsl:when>
<xsl:otherwise>
nie możliwe
</xsl:otherwise>
</xsl:choose>
Jeśli więcej niż jeden warunek jest
spełniony, to i tak wstawiona zostanie zawartość tylko
pierwszej instrukcji xsl:when z prawdziwą wartością
atrybutu test.
Nam jednak wystarczy prostsza intrukcja xsl:if.
Użyjemy także, wspomnianej już, specjalnej instrukcji do
tworzenia atrybutów - xsl:attribute. Umieszcza się ją
wewnątrz elementu, który ma zawierać atrybut. Oto przykład
użycia do tworzenia parametru src HTML'owego znacznika IMG
(wstawianie grafiki):
<xsl:template match="grafika">
<img><xsl:attribute name="src">
<xsl:value-of select="@źródło"/>.gif
</xsl:attribute></img>
</xsl:template>
Jak widać, parametr name instrukcji xsl:attribute
zawiera nazwę tworzonego atrybutu. Natomiast treść parametru
znajduje się w zawartości tej instrukcji. Oto kod
sprawdzający, czy atrybut dane ma wartość fikcyjne.
Jeśli tak, to do elementu tr zostanie dodany parametr
zmieniający kolor tła:
<tr>
<xsl:if test="@dane='fikcyjne'">
<xsl:attribute name="style">background-color: #EAEAEA</xsl:attribute>
</xsl:if>
</tr>
Kod ten nie zadziała w Internet Explorerze.
Przyczyną są problemy w obsłudze zapytania z parametru test.
Niestety nawet najnowsza wersja pasera Microsoft'u (MSXML4) nie
jest w stanie tego zinterpretować.
Tak oto będzie więc wyglądać udoskonalona sekcja
obsługi elementu człowiek:
<xsl:template match="człowiek">
<tr>
<xsl:if test="@dane='fikcyjne'">
<xsl:attribute name="style">background-color: #EAEAEA</xsl:attribute>
</xsl:if>
<td><xsl:value-of select="@dane"/></td>
<td><xsl:value-of select="imie"/></td>
<td><xsl:value-of select="nazwisko"/></td>
<xsl:variable name="poczta">
<xsl:value-of select="poczta"/>
</xsl:variable>
<td><a href="mailto:{$poczta}"><xsl:value-of select="$poczta"/></a></td>
<xsl:variable name="web">
<xsl:value-of select="web"/>
</xsl:variable>
<td><a href="{$web}"><xsl:value-of select="$web"/></a></td>
</tr>
</xsl:template>
Podgląd wygenerowanego pliku HTML:
![[8111 bajtów]](xml/xslattif.gif) |
Opera i wczytany z pliku HTML wynik przekształceń |
|