Parametere
Saxon
lxml
XSLT
Børre Stenseth

Parametere

Hva

XSL har et parameterbegrep. Vi kan definere parametre og bruke dem f.eks. når vi kaller en template.

Med referanse til data fra Olympiadeeksempelet, se modulen Olympiade .

Fila i sin helhet

Vi kan lage konstruksjoner av følgende type:

...
<xsl:apply-templates select="IOC/OlympicGame" >
   <xsl:with-param name="sted" select="'Barcelona'"/>
</xsl:apply-templates>
...
<xsl:template match="OlympicGame">
   <xsl:param name="sted"/>
   <xsl:if test="$sted=@place">
   ... do something ...
   </xsl:if>
</xsl:template>
...

Slike konstruksjoner er parallelle til måten vi bruker parametre på ved prosedyre-kall i andre programmeringsspråk. Parameterstyring av templates er interessant i seg selv, men det løser ikke det problemet vi stadig møter når vi skal planlegge XSLT-transformasjoner. Hvordan skal vi kunne parameterstyre hele transformasjonen ?

Hvis vi f.eks. ønsker å ekstrahere resultatene fra 200m i Barcelona fra vår lange XML-fil med flere olymiader og flere øvelser, har vi et problem. Det virker litt tungvindt å skrive en transformasjon for alle mulige kombinasjoner av sted og øvelse. Vi kan nærme oss problemet ved å se på en konstruksjon der vi innfører to "globale parametere":

   <xsl:param name="sted" select="'Barcelona'"/>
   <xsl:param name="distanse" select="'200m'"/>

Når vi kaller dem globale så innebærer det at de er definert utenfor alle templates i stilsettet og de er tilgjengelig i hele fila, alle templates. Transformasjonsfila er i sin helhet slik:

<?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" version="1.0" 
     omit-xml-declaration="yes" 
     indent="yes"/>-->
<xsl:output method="html"  
   doctype-system="about:legacy-compat" encoding="UTF-8"  />

<xsl:param name="sted" select="'Barcelona'"/>
<xsl:param name="distanse" select="'200m'"/>

<xsl:template match="/">
<html>
<head>
      <title>Olympics</title>
      <link rel="STYLESHEET" href="olymp.css"/>
</head>
<body>
   <h1>Resultater sprint</h1>
   <xsl:apply-templates select="IOC/OlympicGame[@place=$sted]"/>
   <a href="javascript:history.back()">tilbake</a>
</body>
</html>
</xsl:template>
<xsl:template match="OlympicGame">
    <h2>
   <xsl:value-of select="@place"/> - <xsl:value-of select="@year"/>
   </h2>
     <xsl:apply-templates select="event[@dist=$distanse]"/>
</xsl:template>
<xsl:template match="event">
   <h3><xsl:value-of select="@track"/></h3>
   <xsl:apply-templates select="athlet">  
            <xsl:sort select="result" order="ascending"/>
   </xsl:apply-templates>
</xsl:template>
<xsl:template match="athlet">
   <p>
   <xsl:value-of select="result"/> : <xsl:value-of select="name"/>
   </p>
</xsl:template>
</xsl:stylesheet>
Resultatet https://borres.hiof.no/wep/xslt/par/xsl_output.html

Det virker rimelig å ønske seg et transformasjonsverktøy som kan ta en liste av parametre som input, tolke disse som globale parametre og så foreta transformasjonen. Og heldigvis, slike finnes. Vi skal se på et Pythonverktøy: lxml. I modulen På klienten kan du dessuten se hvordan vi kan gjøre dette på kliente.

lxml

lxml [1] synes å være det biblioteket som gir best og enklest funksjonalitet når det gjelder XSLT og XPath i Python.

Eksempel Olympiade

Vi tar utgangspunkt i samme xml-darta som ovenfor rådata og bruker den samme transformasjonen som er sitert ovenfor, med to globale parametre.

Vi bruker et Pythonprogram som cgi-script for å utføre transformasjonen basert på de ønskene (sted og øvelse) vi sender inn fra vår HTML-side.

#! /usr/bin/python
import sys,cgi
from lxml import etree
#import cgitb; cgitb.enable()

#-------------------------------------------------------------
# Testing lxml, parametric xslt
# B. Stenseth  2014
#-------------------------------------------------------------
def produce(place,distance):
    # fixed filenames 
    xmlfile='../ol/all_results.xml'
    xsltfile='olymp_x.xslt'
    htmlfile='result.html'
    xmlTree=etree.parse(xmlfile)
    xsltTree=etree.parse(xsltfile)
    transform=etree.XSLT(xsltTree)
    d=etree.XSLT.strparam(distance)
    p=etree.XSLT.strparam(place)
    resultTree=transform(xmlTree,sted=p,distanse=d)
    print str(resultTree)
#-----------------------------------------
print 'Content-type: text/html\n'
form=cgi.FieldStorage()
# what was thew question again ?
place=''
distance=''
try:
   place=form['1'].value
except:
   place='Barcelona'
try:
   distance=form['2'].value
except:
   distance='400m'
produce(place,distance)
Test her https://borres.hiof.no/wep/xslt/par/lxmlstarter.html

Eksempel Fotball

Vi tar utgangspunkt i en XML-fil som inneholder resultatene fra Engelsk Premier League sesongen 2001/2002. Fila er bygd opp slik:

<?xml version="1.0" encoding="utf-8"?>
<liga>
	<land>England</land>
	<serie>Premier League</serie>
	<sesong>2001/2002</sesong>
	<kamp>
		<dato>18.08.2001</dato>
		<hlag>Charlton</hlag>
		<hmal>1</hmal>
		<blag>Everton</blag>
		<bmal>2</bmal>
	</kamp>
	<kamp>
		<dato>18.08.2001</dato>
		<hlag>Derby</hlag>
		<hmal>2</hmal>
		<blag>Blackburn</blag>
		<bmal>1</bmal>
	</kamp>
	...
</liga>

Fila i sin helhet

Vi skriver en transformasjon som ekstraherer resultater avhengig av angitt hjemmelag og bortelag:

<?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" encoding="UTF-8"  />
<xsl:param name="hjemmelag" select="'Chelsea'"/>
<xsl:param name="bortelag" select="'Arsenal'"/>
    
<xsl:template match="/">
    <html>
    <head>
    <title>Fotball</title>
    </head>
    
    <body>
    <h1><xsl:value-of select="liga/land"/></h1>
    <h2>
    <xsl:value-of select="liga/serie"/> 
     - <xsl:value-of select="liga/sesong"/>
    </h2>
    <xsl:for-each select="liga/kamp">
        <xsl:if test="($hjemmelag='*' and $bortelag='*' ) 
                           or (hlag=$hjemmelag and $bortelag='*') 
                           or ($hjemmelag='*' and blag=$bortelag)
                        or (hlag=$hjemmelag and blag=$bortelag)">
            <xsl:apply-templates select="self::node()"/>
        </xsl:if>
    </xsl:for-each>
   <a href="javascript:history.back()">tilbake</a>
    </body>
    </html>
</xsl:template>
<xsl:template match="kamp">
    <div>
    <xsl:value-of select="dato"/>:
    <xsl:value-of select="hlag"/>-<xsl:value-of select="blag"/> : 
    <xsl:value-of select="hmal"/>-<xsl:value-of select="bmal"/>
    </div>
</xsl:template>
    
</xsl:stylesheet>

Vi bruker et Python-script, for å utføre transformasjonen basert på de ønskene vi sender inn.

#! /usr/bin/python
import sys,cgi
from lxml import etree
#import cgitb; cgitb.enable()
#-------------------------------------------------------------
# Testing lxml, parametric xslt
# Parameters are:
# hjemmelag (*: wildcard)
# bortelag  (*: wildcard)
# B. Stenseth  2014
#-------------------------------------------------------------
def produce(hjemme,borte):
    # fixed filenames 
    xmlfile='eng0_01_02.xml'
    xsltfile='trans1.xslt'
    htmlfile='result.html'
    xmlTree=etree.parse(xmlfile)
    xsltTree=etree.parse(xsltfile)
    transform=etree.XSLT(xsltTree)
    h=etree.XSLT.strparam(hjemme)
    b=etree.XSLT.strparam(borte)
    resultTree=transform(xmlTree,hjemmelag=h,bortelag=b)
    print str(resultTree)
#-----------------------------------------
print "Content-type: text/html\n"
form=cgi.FieldStorage()
# what was the question again ?
hjemme=''
borte=''
try:
   hjemme=form['hjemmelag'].value
except:
   hjemme='*'
try:
   borte=form['bortelag'].value
except:
   borte='*'

produce(hjemme,borte)
Test her https://borres.hiof.no/wep/xslt/par/soccertestlxml.html
Referanser
  1. lxml - XML and HTML with Python lxml.de/ 03-03-2014