Gern stehe ich zur fachlichen Unterstützung in XML-Technologien, C#.NET, VisualBasic.NET und Java zur Verfügung. Sprechen Sie mich einfach an: Mail oder ☎ 0151 . 750 360 61


XSL-Übersicht / xsl:param

xsl:param

xsl:param

xsl:param kommt zum Einsatz bei globaler und lokaler Parameterübergabe.

Auf dieser Seite:

Globale Parameter

Die globale Parameterübergabe kommt zum Einsatz beim Start von XSL-Transformationen. Sie können dem XSL-Prozessor Parameter mitgegeben, die dieser in der Transformationslogik verarbeiten soll.

Nehmen Sie das folgende xsl:stylesheet, in dem die beiden Parameter xmlparamtest1 und xmlparamtest2 auf oberster Ebene deklariert wurden. Diese beiden Parameter sollen mit xsl:value-of select unmittelbare Bestandteile des Ausgabestroms werden.

Der dritte Parameter externeDateiName enthält die Adresse einer separaten Datei, die in xsl:template name="LiesAusDatei" mit der doc()-Funktion ausgelesen wird (siehe ).


<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, ich nenne sie "Liste1.xml" (dieser Name wird erst im Transformationsaufruf übergeben), muss einen bestimmten Aufbau haben, damit die XPath-Adressierung im xsl: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 Sie sich eine solche:


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
  // Singleton Pattern
  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 xs:string infrage.


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

Würden Sie testweise einen der Parameter als xs:integer definieren, so erhielten Sie 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 Sie auch an der korrekten Ausführung der position()-Nummerierung.

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 kann die Wartbarkeit erhöhen.


<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, dass 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 und Informationsverlust, weil es bei ungenauer/fehlerhafter Programmierung immer wieder vorkommt, dass das System zur Laufzeit statt des vermuteten Einzelwerts (auf dem die Logik beruht) tatsächlich 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: Nummerierung bei xsl:apply-templates

Zunächst nicht notwendig ist die Parameterübergabe bei der Nummerierung 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, sodass (zumindest zu diesem Zweck) kein Parameter übergeben werden muss.


<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 Nummerierung eine Parameterübergabe zwingend erforderlich, weil die position()-Funktion bei jedem Aufruf den Wert "1" ermittelt. In diesem Fall muss der Aufruf lauten:


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

... verbunden mit dem modifizierten Template, das nunmehr eine Parameterübergabe einfordert.


<xsl:template match="Kauf" mode="nummerierung">
  <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 / 7. April 2018



Fragen? Anmerkungen? Tipps?

Bitte nehmen Sie Kontakt zu mir auf.






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