Basis
Vi trenger noe råmateriale å arbeide med. Gjennomgangseksempelet i denne modulen vil være en bokliste som er bygget opp slik:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE booklist SYSTEM "bokdok.dtd">
<?xml-stylesheet type="text/xsl" href="boktrans.xslt"?>
<booklist>
<book isbn="" pages="">
<title/>
<course/>
<category/>
<author/>
<publisher/>
<year/>
<comment/>
</book>
<!-- more books -->
</booklist>
I de tre første linjene angir vi:
- at dette er en XML-fil og hvordan den er kodet
- at fila er av en bestemt type. Vi har laget en DTD-fil og kan validere den. Dette er strengt tatt ikke nødvendig for de transformasjonsøvelsene vi skal gjøre.
- at vi skal kople til en XSL-transformasjon. Navnet på XSL-fila vil endre seg etterhvert som vi skal gjøre ulike typer transformasjoner i det følgende.
Selve innholdet er en liste med bøker. Her kan du se et eksempel på en fil med litt innhold: page1/bok1.xml.
For enkelhets skyld skal vi holde oss til transformasjoner fra XML til HTML. Du vil finne mange eksempler på andre typer transformasjoner i dette materialet, blandt annet i eksempelet Olympiade . Vi transformerer til HTML for at vi kan inspisere resultatet direkte i en nettleser som kan transformere automatisk. Sørg for at du bruker en slik nettleser når du arbeider med disse eksemplene. Alternativt må du bruke en av verktøyene som er beskrevet i modulen Verktøy, f.eks. XMLSpy, og transformere før du ser på resultatet i en hvilken som helst nettleser som kan tolke den HTML-koden din transformasjon leverer.
Enkel liste
Vi skriver en minimal transformasjon som bare skriver ut en liste over alle boktitlene, uten noen form for formattering:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"
omit-xml-declaration="yes"
indent="yes"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
encoding="utf-8"/>
<xsl:template match="/">
<html>
<head>
<title>liste</title>
</head>
<body>
<h1>List of books</h1>
<xsl:for-each select="booklist/book">
<p>
<xsl:value-of select="title"/>
</p>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Boklista transformert med denne transformasjonen direkte i en nettleser:
Eller se det transformerte resultatet direkte:
Vi merker oss følgende:
- XSL-fila er en XML-fil. Det vil si at XSL er et språk i XML-familien.
- Vi definerer et namespace for XSLT, med kortnavnet xsl. Vi kan bruker xsl som prefix når vi skal angi elementer som er XSL-elementer.
- Vi angir hva slags output vi ønsker, og hvordan den skal kodes.
- Vi angir en template som skal knyttes til det øverste nivået i den xml-fila vi skal transformere. / angir rota i treet. Vi kan ha flere templates.
Inne i templaten gjenkjenner vi HTML-kode. Denne teksten genereres slik som den er i resultatet av transformasjonen. Det eneste vi bruker fra den XML-fila vi jobber på finner vi ved kodesegmentet
<xsl:for-each select="booklist/book">
<p>
<xsl:value-of select="title"/>
</p>
</xsl:for-each>
Vi befinner oss i en template som er koplet til rota i XML-strukturen og vi angir at vi vil ha en løkke over de nodene som heter book og som er barn av booklist. Når vi så har kommet inn i selve løkka, kan vi etterpå si at vi skal ha tak i verdien, innholdt, i elementet title Dette rører ved noe av det som er sentralt i XSLT. Vi ser at uttrykket boolist/book minner om måten vi skriver deler av en filpath på. I XSL-terminologi har vi oppgitt en XPath. Du vil trolig oppleve at det som skaper mest hodebry i XSL er å finne riktige slike path-uttrykk for å "peke på" elementer som er barn, søsken eller foreldre til andre elementer. Vi må hele tid finne den pathen i forhold til det stedet vi befinner oss, konteksten eller contekst node.
Her er det slik at vi endrer contekst i for-løkka og kan derfor angi title direkte, uten noen kvalifiserende path.
Templates
Vi tar for oss det samme problemet som ovenfor, å skrive ut en liste av boktitler. Denne gangen formulerer vi oss litt annerledes. Vi lager en egen template for bokutskrift. Vi får altså en template i tillegg:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"
omit-xml-declaration="yes"
indent="yes"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
encoding="utf-8"/>
<xsl:template match="/">
<html>
<head>
<title>liste</title>
</head>
<body>
<h1>Booklist</h1>
<xsl:for-each select="booklist/book">
<xsl:apply-templates select="self::node()"/>
</xsl:for-each>
</body>
</html>
</xsl:template>
<xsl:template match="book">
<p>
<xsl:value-of select="title"/>
</p>
</xsl:template>
</xsl:stylesheet>
Boklista transformert med denne transformasjonen direkte i en nettleser:
Eller se det transformerte resultatet direkte:
Den nye templaten er laget for /booklist/book. Når vi går i løkka ber vi om å få anvendt templates som passer til den konteksten vi er i, self::node(). Vi kan erstatte self::node() med ., punktum, som kortform.
Se på alternativet nedenfor:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"
omit-xml-declaration="yes"
indent="yes"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
encoding="utf-8"/>
<xsl:template match="/">
<html>
<head>
<title>minimum</title>
</head>
<body>
<xsl:apply-templates select="booklist/book"/>
</body>
</html>
</xsl:template>
<xsl:template match="//book">
<p>
<xsl:value-of select="title"/>
</p>
</xsl:template>
</xsl:stylesheet>
Vi har droppet for-løkka og bestilt en template for booklist/book. Vi får en automatisk gjennomgang av alle bøker. Vi kan lese select="booklist/book" slik: "hent alle book-noder under booklist".
Vi har dessuten generalisert den andre templaten slik at den matcher bok. Det vil si alle boknoder uansett hvem de er barn av. I dette tilfellet gjør det ingen forskjell sidan vår struktur bare har en type bokelement.
Litt stil
Vi ser av eksemplene ovenfor at vi kan generere HTML etter ønske, i hvert fall så langt. Den HTML-koden vi lager gir ikke noe spesielt pent resultat og HTML-sidene er av ubestemt art. Vi ønsker å lage HTML 4 transitional og vi ønsker å kople til et stilsett. Vi gjør dette ved å endre transformasjonsfila slik at prologen i HTML-fila blir slik vi ønsker:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"
omit-xml-declaration="no"
indent="yes"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
encoding="utf-8"/>
<xsl:template match="/">
<html>
<head>
<!-- link to stylesheet -->
<link rel="STYLESHEET" href="bokstil.css"/>
<title>bokliste</title>
</head>
<body>
<h1>Liste over alle bøker</h1>
<ul>
<xsl:apply-templates select="booklist/book"/>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="book">
<li class="enkel_liste">
<xsl:value-of select="title"/>
</li>
</xsl:template>
</xsl:stylesheet>
Boklista transformert med denne transformasjonen direkte i en nettleser:
Eller se det transformerte resultatet direkte:
Vi ser dessuten at vi har skrevet om slik at vi får en overskrift og en liste med en egen stil, enkel_liste.
Bedre liste
Vi beholder i hovedsak strukturen i den transformasjonen vi brukte over, men vi vil endre templaten for å skrive ut en bok slik at vi får ut mer informasjon om hver bok. Vi vil dessuten skrive ut mer informasjon i toppen av siden.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"
omit-xml-declaration="yes"
indent="yes"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
encoding="utf-8"/>
<xsl:template match="/">
<html>
<head>
<!-- link to stylesheet -->
<link rel="STYLESHEET" href="bokstil.css" />
<title>bokliste</title>
</head>
<body>
<h1>Liste over alle bøker</h1>
<xsl:apply-templates select="booklist/book"/>
</body>
</html>
</xsl:template>
<xsl:template match="book">
<div class="booktitle"><xsl:value-of select="title"/></div>
<div><xsl:value-of select="author"/></div>
<div><xsl:value-of select="publisher"/>,
<xsl:value-of select="year"/></div>
<div>Isbn: <xsl:value-of select="@isbn"/></div>
<div class="kommentar"><xsl:value-of select="comment"/></div>
</xsl:template>
</xsl:stylesheet>
Boklista transformert med denne transformasjonen direkte i en nettleser:
Eller se det transformerte resultatet direkte:
Utvalgt liste
Vi ønsker å endre transformasjonen slik at vi bare får ut bøker som har kategori XML. Foruten at vi endrer overskriften, så endrer vi løkka som går gjennom bøkene slik:
<xsl:apply-templates select="booklist/book[category='XML']"/>
Boklista transformert med denne transformasjonen direkte i en nettleser:
Eller se det transformerte resultatet direkte:
Her ser vi at vi har et litt mer komplisert XPath-uttrykk. Vi har lagt til [category='XML'] som spesifiserer et utvalg basert på de aktuelle nodenes verdier.
HTML 5
I alle eksemplene ovenfor finner du i XSLT-filene en setning som ser slik ut:
<xsl:output method="html" omit-xml-declaration="yes" indent="yes" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" encoding="utf-8"/> ...
Det vi angir her er altså at vi skal lage HTML, og vi angir dokumenhttype og encoding. Vi sier dessuten at vi skal droppe XML-headeren. Dette er en grei måt å lage kontrollert HTML strict.
Dersom vi ønsker å lage HTML5, kan vi løse dette etter følgende mønster:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"
omit-xml-declaration="yes"
indent="yes" />
<xsl:template match="/">
<xsl:text disable-output-escaping='yes'><!DOCTYPE html></xsl:text>
<html>
<head> </head>
<body> </body>
</html>
</xsl:template>
</xsl:stylesheet>
Vi gjentar det siste eksempelet overfor med følgende transformasjon:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"
omit-xml-declaration="yes"
indent="yes" />
<xsl:template match="/">
<xsl:text disable-output-escaping="yes"><!DOCTYPE html></xsl:text>
<html>
<head>
<meta charset="UTF-8"/>
<link rel="STYLESHEET" href="bokstil.css" />
<title>bokliste</title>
</head>
<body>
<h1>Liste over alle XML-bøker</h1>
<xsl:apply-templates select="booklist/book[category='XML']"/>
</body>
</html>
</xsl:template>
<xsl:template match="book">
<div class="booktitle"><xsl:value-of select="title"/></div>
<div><xsl:value-of select="author"/></div>
<div><xsl:value-of select="publisher"/>,
<xsl:value-of select="year"/></div>
<div>Isbn: <xsl:value-of select="@isbn"/></div>
<div class="kommentar"><xsl:value-of select="comment"/></div>
</xsl:template>
</xsl:stylesheet>
Boklista transformert med denne transformasjonen: