XSL-Übersicht / xsl:param

xsl:param

xsl:param

➪ xsl:param kommt zum Einsatz bei globaler (Transformationsprozeß) und lokaler (xsl:call-template, xsl:apply-templates, xsl:with-param) Parameterübergabe.

Globale Parameter

Zusätzlich zur lokalen Parameterdefinition können wir auch mit globalen Parametern arbeiten, deren Werte dem XSL-Prozessor mitgegeben werden.

Auch hier sagt ein Beispiel mehr als tausend Worte. Nehmen wir das folgende xsl:stylesheet, in dem die beiden Parameter "xmlparamtest1" und "xmlparamtest2" auf oberster Ebene deklariert wurden. Diese beiden Parameter sollen mit Hlfe von "xsl:value-of select" Bestandteil des Ausgabestroms werden.

Der dritte Parameter "externeDateiName" enthält die Adresse einer separaten Datei, die im Template name="LiesAusDatei" ausgelesen wird.


<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">
  <xsl:output method="xml" indent="yes"/>
  <xsl:param name="xmlparamtest1"/>
  <xsl:param name="xmlparamtest2"/>
  <xsl:param name="externeDateiName" as="xs:string"/>
  <xsl:template match="/">
    <rootelement>
      <ersterParameter>
        <xsl:value-of select="$xmlparamtest1"/>
      </ersterParameter>
      <zweiterParameter>
        <xsl:value-of select="$xmlparamtest2"/>
      </zweiterParameter>
      <dritterParameter>
        <xsl:call-template name="LiesAusDatei">
          <xsl:with-param name="pKey">2</xsl:with-param>
        </xsl:call-template>
      </dritterParameter>
    </rootelement>
  </xsl:template>
  <xsl:template name="LiesAusDatei">
    <xsl:param name="pKey"/>
    <xsl:variable 
         name="vexterneDatei" 
         select="doc($externeDateiName)"/>
    <xsl:value-of 
         select="$vexterneDatei/Root/Value[@Key =$pKey]/text()"/>
  </xsl:template>
</xsl:stylesheet>

Die externe Datei, nennen wir sie "Liste1.xml" (dieser Name wird erst im Transformationsaufruf übergeben), muß einen bestimmten Aufbau haben, damit die XPath-Adressierung im Template name="LiesAusDatei" und dem hier übergebenen Parameter "pKey" (dieser wird im xsl:template match="/" via <xsl:with-param name="pKey">2</xsl:with-param> mit "2" übergeben) korrekt funktioniert.


<Root>
    <Value Key="1">MOIN</Value>
    <Value Key="2">Alles senkrecht?</Value>
</Root>

In Java liest sich der Code für eine XSL-Transformation beispielsweise so:


public static void XSLtransform(
       String xsltFileName, 
       String xmlsourceFileName, 
       String xmldestFileName,  
       ArrayList<XMLParameterkeyvalue> parameterliste) {
    try {      
      TransformerFactory tfactory 
             = TransformerFactory.newInstance();
      Transformer transformer
             = tfactory.newTransformer(
                   new StreamSource(
                       new File(xsltFileName)));
      // hier werden die Werte aus der ArrayList übernommen
      for(XMLParameterkeyvalue x : parameterliste){
        transformer.setParameter(
                   x.getParamname(), 
                   x.getParamwert());
      }      
      transformer.transform(
             new StreamSource(
                 new File(xmlsourceFileName)),
             new StreamResult(
                 new File(xmldestFileName)));
    } catch (TransformerConfigurationException e) {    
    } catch (TransformerException e) {
    }
}

Der in Java übergebene Parameter "parameterliste" ist vom generischen Typ "ArrayList<XMLParameterkeyvalue>". Da es eine solche Java-Klasse standardmäßig nicht gibt, schreiben wir sie uns schnell:


class XMLParameterkeyvalue{
  private String paramname;
  private String paramwert;
  public String getParamname() {
    return paramname;
  }
  public String getParamwert() {
    return paramwert;
  }
  public XMLParameterkeyvalue(
         String paramname, String paramwert) {
    super();
    this.paramname = paramname;
    this.paramwert = paramwert;
  }
  // java.util.ArrayList
  private static ArrayList<XMLParameterkeyvalue> 
       _parameterliste = new ArrayList<XMLParameterkeyvalue>();
  public static void Add(String paramname, String paramwert){
    _parameterliste.add(
       new XMLParameterkeyvalue(paramname, paramwert));
  }
  public static ArrayList<XMLParameterkeyvalue> getParameterliste(){
    return _parameterliste;
  }  
}

Damit reduziert sich der Aufruf in "public static void main(String[] args)" erstens darauf, die Parameterliste mit den erwarteten Namen "xmlparamtest1", "xmlparamtest2" und "externeDateiName" zu vervollständigen und diese in den static-Methodenaufruf einzubinden.


XMLParameterkeyvalue.Add("xmlparamtest1", "Hallo Leute von heute!");
XMLParameterkeyvalue.Add("xmlparamtest2", "Wie gehts denn so?");
XMLParameterkeyvalue.Add("externeDateiName", "C:/wg/Liste1.xml");
XSLtransform("C:/wg/globaleParameteruebergabe.xsl",     
    "C:/wg/Ort_Elemente.xml", 
    "C:/wg/erg.xml", 
    XMLParameterkeyvalue.getParameterliste());

Das Ergebnis sieht (wen wundert's?) so aus:


<rootelement>
  <ersterParameter>Hallo Leute von heute!</ersterParameter>
  <zweiterParameter>Wie gehts denn so?</zweiterParameter>
  <dritterParameter>Alles senkrecht?</dritterParameter>
</rootelement>

Kurzer Hinweis am Rande: natürlich ist es ab XSLT 2.0 auch möglich, die Parametertypen zu definieren. Nach Sachlage kommt hier ein xs:string in Frage.


<xsl:param name="xmlparamtest1" as="xs:string"/>
<xsl:param name="xmlparamtest2" as="xs:string"/>

Würden wir testweise einen der Parameter als xs:integer definieren, so erhalten wir vom Prozessor einen freundlichen Gruß in Form einer ValidationException:


<xsl:param name="xmlparamtest2" as="xs:integer"/>
ValidationException:
Cannot convert string "Wie gehts denn so?" to an integer

Lokale Parameter: Einzelwert bei xsl:call-template

Wie in beschrieben, ist die Auslagerung einer Logik in ein benanntes Template "xsl:call-template", die ebenso gut in xsl:for-each oder in xsl:template match stattfinden könnte, zwar möglich, aber nicht in jedem Fall sinnvoll.

Sinnvoll ist das meiner Ansicht nach nicht, wenn die interne XPath-Adressierung des benannten Templates direkt vom XML-Input abhängt. Dies schafft zusätzliche Abhängigkeiten, die die Wartbarkeit des Gesamtprogramms nicht verbessern.

Einen solchen Fall möchte ich hier beschreiben.


<xsl:template match="/">
  <rootelement>
   <xsl:for-each select="//Kauf">
    <xsl:call-template name="Einzelwert"/>
   </xsl:for-each>
  </rootelement>
</xsl:template>

Das obige "Haupt"-Template ruft ein benanntes Template "Einzelwert" auf, und zwar ohne Parameterübergabe.


<xsl:template name="Einzelwert">  
  <xsl:choose>
   <xsl:when test="anzahl &gt; 10">
    <viel nr="{position()}">
     <xsl:value-of select="anzahl"/>
    </viel>
   </xsl:when>
   <xsl:otherwise>
    <wenig nr="{position()}">
     <xsl:value-of select="anzahl"/>
    </wenig>
   </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Bei der Programmausführung erhält das benannte Template "Einzelwert" jedes Mal den Scope des aufrufenden Elements; das erkennen wir auch an der korrekten Ausführung der "position()"-Numerierung.

Dieselbe Logik könnte (wie ich finde: übersichtlicher und leichter wartbar) in einem einzelnen Aufruf implementiert werden:


<xsl:template match="/">
  <rootelement>
   <xsl:for-each select="//Kauf">
    <xsl:choose>
     <xsl:when test="anzahl &gt; 10">
      <viel nr="{position()}">
       <xsl:value-of select="anzahl"/>
      </viel>
     </xsl:when>
     <xsl:otherwise>
      <wenig nr="{position()}">
       <xsl:value-of select="anzahl"/>
      </wenig>
     </xsl:otherwise>
    </xsl:choose>
   </xsl:for-each>
  </rootelement>
</xsl:template>

Etwas anderes ist es, wenn das benannte Template nicht direkt vom XML Input abhängig ist, sondern über Parameter gesteuert wird (siehe "Einzelwert_2"). Das erhöht die Wartbarkeit beträchtlich.


<xsl:template match="/">
  <rootelement>
   <xsl:for-each select="//Kauf">
    <xsl:call-template name="Einzelwert_2">
     <xsl:with-param name="p" select="anzahl"/>
    </xsl:call-template>
   </xsl:for-each>
  </rootelement>
</xsl:template>

ist jedoch, daß der übergebene Parameter auch von einem Datentyp ist, den das benannte Template voraussetzt, um Programmierfehler mit weitreichenden Konsequenzen zu vermeiden.


<xsl:template name="Einzelwert_2">
  <xsl:param name="p" as="xs:integer" />
  <xsl:choose>
   <xsl:when test="$p &gt; 10">
    <viel nr="{position()}">
     <xsl:value-of select="$p"/>
    </viel>
   </xsl:when>
   <xsl:otherwise>
    <wenig nr="{position()}">
     <xsl:value-of select="$p"/>
    </wenig>
   </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Hier lohnt sich die XML-Schema-basierte Typkontrolle. Wer darauf verzichtet, riskiert stundenlange Fehlersuche, weil es bei ungenauer Programmierung immer wieder vorkommt, daß das System zur Laufzeit statt des erwarteten Einzelwerts eine komplexe Nodeliste als Parameter übergibt.

Zudem wäre die Parameter-Übergabe einer komplexen Nodeliste, die ursprünglich aus dem XML Input stammt, wieder kontraindiziert für die Wartbarkeit der Anwendung. Vgl.

Lokale Parameter: Numerierung bei xsl:apply-templates

Zunächst nicht notwendig ist die Parameterübergabe bei der Numerierung im Zusammenhang mit xsl:apply-templates.


<xsl:template match="/">
  <rootelement>
   <xsl:apply-templates select="//Kauf"/>
  </rootelement>
</xsl:template>

Bei diesem Aufruf wird auch die Position korrekt übernommen, so daß (zumindest zu diesem Zweck) kein Parameter übergeben werden muß.


<xsl:template match="Kauf">
  <xsl:choose>
   <xsl:when test="anzahl &gt; 10">
    <viel nr="{position()}">
     <xsl:value-of select="anzahl"/>
    </viel>
   </xsl:when>
   <xsl:otherwise>
    <wenig nr="{position()}">
     <xsl:value-of select="anzahl"/>
    </wenig>
   </xsl:otherwise>
  </xsl:choose> 
</xsl:template>

Lautet der Aufruf dagegen ...


<xsl:template match="/">
  <rootelement>
   <xsl:for-each select="//Kauf">
    <xsl:apply-templates select="."/>
   </xsl:for-each>
  </rootelement>
</xsl:template>

... so ist für die korrekte Numerierung eine Parameterübergabe zwingend erforderlich, weil die position()-Funktion bei jedem Aufruf den Wert "1" ermittelt. In diesem Fall muß der Aufruf lauten:


<xsl:template match="/">
  <rootelement>
   <xsl:for-each select="//Kauf">
    <xsl:apply-templates select="." mode="numerierung">
     <xsl:with-param name="pos" select="position()"/>
    </xsl:apply-templates>
   </xsl:for-each>
  </rootelement>
</xsl:template>

... verbunden mit dem modifizierten <xsl:template match="Kauf">, das nunmehr eine Parameter


<xsl:template match="Kauf" mode="numerierung">
  <xsl:param name="pos"/>
  <xsl:choose>
   <xsl:when test="anzahl &gt; 10">
    <viel nr="{$pos}">
     <xsl:value-of select="anzahl"/>
    </viel>
   </xsl:when>
   <xsl:otherwise>
    <wenig nr="{$pos}">
     <xsl:value-of select="anzahl"/>
    </wenig>
   </xsl:otherwise>
  </xsl:choose>
</xsl:template>

wg / 4. März 2018



Fragen? Anmerkungen? Tips?

Bitte nehmen Sie Kontakt zu mir auf:

Vorname
Nachname
Mailadresse







Vielen Dank für Ihr Interesse an meiner Arbeit.


V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn

☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de

www.wilfried-grupe.de/XSL_Param.html