Basis
Vi stoler på nettleserens evne til å foreta transformasjoner "on-the-fly". Oppsettet i denne modulen er da at vi gir nettleseren adressen til en XML-fil. Den andre linja i XML-fila angir en transformasjon, en XSL-fil:
<?xml version="1.0" encoding="utf-8"?> <?xml-stylesheet type="text/xsl" href="booktrans.xslt"?> <booklist> <book isbn="123456" pages="234"> ..... </book> <!-- more books --> </booklist>
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 (boktrans.xslt) 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: 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. Transformasjonen bruker output-metode HTML og den lager XHTML 1.0 Strict. Vi lager XHTML strict i de første eksemplene for å demonstrere de grunnleggende mekanismene. Mot slutten av modulen ser vi på produksjon av HTML5.
<?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. Studer kildekoden og inspect element på denne siden:
Eller se det ferdigtransformerte resultatet direkte, studer kildekoden:
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. Studer kildekoden og inspect element på denne siden:
Eller se det fedig 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 å 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="xml" 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> <meta charset="UTF-8"/> </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. Studer kildekoden og inspect element på denne siden:
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" /> <meta charset="UTF-8"/> <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. Studer kildekoden og inspect element på denne siden:
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
Den nye headeren i HTML, <!DOCTYPE HTML>, er en utfordring når det gjelder å
lage HTML5 dokumenter direkte i en transformasjon. Det er flere måter å løse dette på.
Når vi skal lage en "on-the-fly" løsning i nettleseren er det best å benytte seg av
en litt finurlig konstruksjon som er introdusert nettopp
for å løse dette problemet på en sikker måte:
doctype-system="about:legacy-compat".
<?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" doctype-system="about:legacy-compat"/> <xsl:template match="/"> <html> <head> <title>liste</title> <meta charset="utf-8"/> </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>
Vi gjentar det første enkle eksempelet overfor med denne transformasjonen
Boklista transformert med denne transformasjonen:
Eller se det transformerte resultatet direkte:
Alternativt
En alternativ måte å løse HTML5 problemet på er som følger:
<?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" /> <xsl:template match="/"> <xsl:text disable-output-escaping='yes'><!DOCTYPE html></xsl:text> <html> <head> <title>liste</title> <meta charset="utf-8"/> </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. Som du ser dra vi med oss HTML5-headeren ut på websiden. Sjekk kildekoden og inspiser elementer:
Boklista ferdig transformert med den samme transformasjonen: