Unike lister
Eksempel 1
Anta følgende XML-fil som viser hvilke land en rekke personer (tourist) har besøkt:
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="transtur1.xsl"?> <travellers> <tourist> <first-name>Jens</first-name> <last-name>Pedersen</last-name> <country>Sveits</country> </tourist> <tourist> <first-name>Ole</first-name> <last-name>Tangen</last-name> <country>Sveits</country> <country>Tyskland</country> </tourist> <tourist> <first-name>Marit</first-name> <last-name>Syversen</last-name> <country>Italia</country> <country>Tyskland</country> </tourist> <tourist> <first-name>Gro</first-name> <last-name>Borgen</last-name> <country>Italia</country> <country>Tyskland</country> <country>Tyrkia</country> </tourist> </travellers>
Som vi ser inngår et land i flere tourist-elementer. Problemstillingen vi reiser er hvilke land inngår i fila og videre hvordan kan vi sortere dataene på land og vise hvem som har besøkt landet.
Vi prøver oss 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" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:output method="xml" indent="yes" encoding="utf-8"/> <xsl:key name="countrynames" match="country" use="."/> <xsl:template match="/"> <root> <xsl:for-each select="//country[generate-id() = generate-id(key('countrynames',.)[1])]"> <xsl:sort order="ascending"/> <xsl:variable name="theCountry" select="."/> <xsl:element name="country"> <xsl:attribute name="name"><xsl:value-of select="$theCountry"/></xsl:attribute> <xsl:apply-templates select="//tourist[country=$theCountry]"/> </xsl:element> </xsl:for-each> </root> </xsl:template> <xsl:template match="//tourist"> <visitor> <xsl:value-of select="last-name"/>,<xsl:value-of select="first-name"/> </visitor> </xsl:template> </xsl:stylesheet>
Det nye i dette er bruken av linjene:
<xsl:key name="countrynames" match="country" use="."/> ... <xsl:for-each select="//country[generate-id() = generate-id(key('countrynames',.)[1])]">
Vi definerer og genererer et nøkkelset countrynames. Det vi egentlig gjør er å lage en hashtabell, for så å løpe gjennom nøklene i en løkke.
Resultatet blir slik:
<?xml version="1.0" encoding="utf-8"?> <root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <country name="Italia"> <visitor>Syversen,Marit</visitor> <visitor>Borgen,Gro</visitor> </country> <country name="Sveits"> <visitor>Pedersen,Jens</visitor> <visitor>Tangen,Ole</visitor> </country> <country name="Tyrkia"> <visitor>Borgen,Gro</visitor> </country> <country name="Tyskland"> <visitor>Tangen,Ole</visitor> <visitor>Syversen,Marit</visitor> <visitor>Borgen,Gro</visitor> </country> </root>
Eksempel 2
Vi bygger ut XML-fila slik at den inneholder rapporter fra turistenes besøk i et land. Vi gjør dette bare med enkel tekst. Vi kunnet godt ha mer kompliserte rapportformater. Merk også at landets navn nå er en attributt til elementet country:
Vi skriver om transformasjonen. Forskjellen er nå at landets navn opptrer som attributt, ikke elementverdi.
og får:
En webløsning
Vi ønsker å etablere en vevside der vi kan lete etter reiserapporter for et land. Siden er bygget opp svært enkelt med en dropdownliste (select element) for å velge land og et div-element der vi viser alle rapportene for dette landet. Det er to felter som skal fylles inn: selectelementet og rapportelementet. Dette ønsker vi å gjøre ved hjelp av et Pythonskript som via lxml [1] transformerer XML-fila slik at vi får ut de to ønskede fragmentene i HTML-format.
Selve HTML-fila er slik:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> <title>Turister</title> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/> <script src="http://www.it.hiof.no/~borres/allround/jquery.js" type="text/javascript"> </script> <script type="text/javascript"> // filling the report element function showChoice(val){ $.ajax({ url:'http://www.it.hiof.no/~borres/cgi-bin/ajax/turister/content.py', data:'country='+val, success:function(data) { $('#report').html(data); }, error:function(data) { $('#report').html('error'); } }); } // filling the select element function initSelect(){ $.ajax({ url:'http://www.it.hiof.no/~borres/cgi-bin/ajax/turister/basicpage.py', success:function(data) { $('#select').html(data); }, error:function(data) { $('#select').html('error'); } }); } </script> </head> <body onload="initSelect()"> <h1>Rapport fra utsendte i </h1> <div id="select"> <!-- select goes here --> </div> <div id="report"> <!-- report goes here --> </div> </body> </html>
Her er valgt å lage et pythonskript for hver av de to behovene: basipage.py som fyller opp select elementet og content.py som fyller opp reiserapportene for det aktuelle landet. De to scriptene er svært enkle og de betjener seg begge av en felles modul utils.py.
basicpage.py
#! /usr/bin/python import cgi import utils T=utils.produce('turister2.xml','basicpage.xsl',None) print 'Content-type: text/html; charset=utf-8\n' print T
content.py
#! /usr/bin/python import cgi import utils form=cgi.FieldStorage() country='Tyrkia' if form.has_key('country'): country=form['country'].value T=utils.produce('turister2.xml','content.xsl',country) print 'Content-type: text/html; charset=utf-8\n' print T
utils.py
from lxml import etree """ transform a file """ def produce(xmlfile,xsltfile,country): tree=etree.parse(xmlfile) xsltree=etree.parse(xsltfile) transform=etree.XSLT(xsltree) if country==None: resulttree=transform(tree) else: c=etree.XSLT.strparam(country) resulttree=transform(tree,selectedCountry=c) return str(resulttree) if __name__=="__main__": # testing T=produce('turister2.xml','basicpage.xsl',None) #T=produce('turister2.xml','content.xsl','Italia') print T
De to aktuelle transformasjonene har samme navn som Pythonskriptet som bruker dem. Transformasjonen som gir select elementet, basicpage.xsl:
<?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" encoding="utf-8" indent="yes" omit-xml-declaration="yes"/> <xsl:key name="countrynames" match="country" use="@name"/> <xsl:template match="/"> <xsl:element name="select"> <xsl:attribute name="name">country</xsl:attribute> <xsl:attribute name="onchange">showChoice(this.value);</xsl:attribute> <xsl:for-each select="//country[generate-id()=generate-id(key('countrynames',@name))]"> <xsl:sort select="@name" order="ascending"/> <xsl:variable name="theCountry" select="@name"/> <xsl:element name="option"> <xsl:attribute name="value"><xsl:value-of select="$theCountry"/></xsl:attribute> <xsl:value-of select="$theCountry"/> </xsl:element> </xsl:for-each> </xsl:element> </xsl:template> </xsl:stylesheet>
Transformasjonen som gir rapportene, content.xsl:
<?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" encoding="utf-8" indent="yes" omit-xml-declaration="yes"/> <xsl:param name="selectedCountry" select="'Tyskland'"/> <xsl:key name="countrynames" match="country" use="@name"/> <xsl:template match="/"> <div> <xsl:for-each select="//country[generate-id()=generate-id(key('countrynames',@name))]"> <xsl:if test="$selectedCountry=@name"> <xsl:variable name="theCountry" select="@name"/> <xsl:apply-templates select="//country[@name=$theCountry]"/> </xsl:if> </xsl:for-each> </div> </xsl:template> <xsl:template match="//country"> <div class="onevisitor"> <h3> <xsl:value-of select="ancestor::tourist/last-name"/> </h3> <p style="margin-left:20px"> <xsl:value-of select="."/> </p> </div> </xsl:template> </xsl:stylesheet>