XPath 3.0, XPath 2.0, XPath 1.0 / XPath-Achsen / XPath-Operatoren

XPath-Operatoren

XPath-Operatoren

➪ Mit XPath können auch Vergleiche, nummerische oder boolesche Rechenoperationen durchgeführt werden.

Auf dieser Seite:

Nummerische Operatoren

+ Addition
- Subtraktion
* Multiplikation
div Division
mod Modulo-Division: gibt den ganzzahligen Rest einer Division zurück. Beispiel: <xsl:if test="position() mod 2 = 0">...</xsl:if> prüft, ob die aktuelle Position eines Elements ohne Rest durch 2 teilbar, also eine gerade Zahl ist.

Was so simpel aussieht, kann im Einzelfall zu Ungenauigkeiten führen. Der folgende Ansatz addiert, subtrahiert, multipliziert und dividiert zwei festgelegte Werte "12.99" und "7". Wenn Sie denken: "Was soll da schon passieren?", dann sehen Sie sich das Ergebnis an.


<xsl:template name="operatordemo">
  <xsl:variable name="v1">12.99</xsl:variable>
  <xsl:variable name="v2">7</xsl:variable>
  <ergebnis>
    <addition>
      <xsl:value-of select="$v1 + $v2" />
    </addition>
    <subtraktion>
      <xsl:value-of select="$v1 - $v2" />
    </subtraktion>
    <multiplikation>
      <xsl:value-of select="$v1 * $v2" />
    </multiplikation>
    <division>
      <xsl:value-of select="$v1 div $v2" />
    </division>
  </ergebnis>
</xsl:template>

Die Addition der beiden Werte hätte "19.99" als Ergebnis bringen sollen, tatsächlich wird der Wert "19.990000000000002" berechnet. Das automatische Type-Casting kann bei der Berechnung längerer Zahlenreihen zu einem realen Fehler führen, der sich nicht mehr durch Formatierung des Ergebnisses korrigieren lässt.


<ergebnis>
    <addition>19.990000000000002</addition>
    <subtraktion>5.99</subtraktion>
    <multiplikation>90.93</multiplikation>
    <division>1.8557142857142856</division>
</ergebnis>

Hier macht sich die exakte, XML-Schema-basierte Typdefinition sehr bezahlt, die in XSLT 2.0 möglich ist:


<xsl:template name="operatordemo_xsd">
  <xsl:variable name="v1" as="xs:decimal">12.99</xsl:variable>
  <xsl:variable name="v2" as="xs:decimal">7</xsl:variable>
  <ergebnis>
    <addition>
      <xsl:value-of select="xs:decimal($v1 + $v2)" />
    </addition>
    <subtraktion>
      <xsl:value-of select="xs:decimal($v1 - $v2)" />
    </subtraktion>
    <multiplikation>
      <xsl:value-of select="xs:decimal($v1 * $v2)" />
    </multiplikation>
    <division>
      <xsl:value-of select="xs:decimal($v1 div $v2)" />
    </division>
  </ergebnis>
</xsl:template>

Hier sehen Sie korrekte Ergebnisse, die bei der Addition und Division vom vorhergehenden Ergebnis abweichen.


<ergebnis>
      <addition>19.99</addition>
      <subtraktion>5.99</subtraktion>
      <multiplikation>90.93</multiplikation>
      <division>1.855714285714285714</division>
</ergebnis>

Möglicherweise sind Sie der Ansicht, diese marginalen Abweichungen nicht beachten zu müssen, weil sie sich in der formatierten Darstellung nicht bemerkbar machen. Okay, das mag in Ihrem Fall so sein. Schwierig kann es jedoch werden, wenn zwei Werte miteinander verglichen werden sollen und (abhängig von diesem Vergleich) unterschiedliche Konsequenzen nach sich ziehen.

Logische Operatoren

< kleiner als. Bei Verwendung im XSL-Umfeld ist eine Konvertierung von "<" in "&lt;" erforderlich.
<= kleiner oder gleich. Hier ist eine Konvertierung in &lt;= erforderlich.
> größer als. Hier ist eine Konvertierung in &gt; erforderlich.
>= größer oder gleich. Hier ist eine Konvertierung in &gt;= erforderlich.
= ist gleich.
!= ist nicht gleich. Beispiel: <xsl:if test="position() != last()"> ...
and und. Verknüpft mehrere Bedingungen.
or oder
not() nicht

Statt der Arbeit mit and kann eine Verwendung von Prädikaten sinnvoll sein. Statt


<xsl:for-each 
     select="//Mensch[Gehalt &gt; 500 and Gehalt &lt; 1000]"> 

... kann man auch schreiben ...


<xsl:for-each 
     select="//Mensch[Gehalt &gt; 500][Gehalt &lt; 1000]">

Wie schon bei den nummerischen Operatoren kann die Arbeit mit Boolean-Tests fehlerhaft werden, etwa wenn zwei Zahlen miteinander verglichen werden und im Hintergrund automatisch ein Type-Cast auf den Datentyp double läuft.


  <xsl:variable name="v1">2.1</xsl:variable>
  <xsl:variable name="v2">3</xsl:variable>
  <ergebnis>
    <xsl:if test="($v1 * $v2) != 6.3">
      <Fehler1 erwartet="6.3">
        <xsl:value-of select="$v1 * $v2" />
      </Fehler1>
    </xsl:if>
    <xsl:if test="($v1 * $v2) &lt; 6.3">
      <Fehler2 erwartet="6.3">
        <xsl:value-of select="$v1 * $v2" />
      </Fehler2>
    </xsl:if>
    <xsl:if test="($v1 * $v2) &gt; 6.3">
      <Fehler3 erwartet="6.3">
        <xsl:value-of select="$v1 * $v2" />
      </Fehler3>
    </xsl:if>
    <xsl:if test="($v1 * $v2) = 6.3">
      <ALLES_OK erwartet="6.3">
        <xsl:value-of select="$v1 * $v2" />
      </ALLES_OK>
    </xsl:if>
  </ergebnis>

In diesem Beispiel erhalten Sie die Ausgabe für die Fehler1 und Fehler3:


<ergebnis>
  <Fehler1 erwartet="6.3">6.300000000000001</Fehler1>
  <Fehler3 erwartet="6.3">6.300000000000001</Fehler3>
</ergebnis>

Ein anderer Prozessor liefert die folgenden Ergebnisse:


<ergebnis>
  <Fehler1 erwartet="6.3">6.3000000000000007</Fehler1>
  <Fehler3 erwartet="6.3">6.3000000000000007</Fehler3>
</ergebnis>

Bemerkenswert (und nur auf den ersten Blick amüsant) fand ich das Ergebnis, das ich mit wieder einem anderen für XSL 1.0, 2.0 und 3.0 erhalten habe:


<ergebnis>
  <Fehler1 erwartet="6.3">6.3</Fehler1>
  <Fehler3 erwartet="6.3">6.3</Fehler3>
</ergebnis>

Würden Sie dagegen die beiden Variablen v1 und v2 auf den Datentyp xs:decimal casten ...


 <xsl:variable name="v1" as="xs:decimal">2.1</xsl:variable>
 <xsl:variable name="v2" as="xs:decimal">3</xsl:variable>

... so wäre das Ergebnis weit ergiebiger:


<ergebnis>
  <ALLES_OK erwartet="6.3">6.3</ALLES_OK>
</ergebnis>

Das ist nicht mehr trivial. Wenn ein Zahlenvergleich (aus teilweise berechneten Werten) grundverschiedene Konsequenzen nach sich zieht, dann müssen Sie sich auch darauf verlassen können, dass das System korrekt arbeitet. Das ist aber nicht zweifelsfrei sichergestellt, wenn eine scheinbar harmlose Rechenoperation suboptimale interne Algorithmen verwendet, die das Ergebnis verfälschen und in der Folge Fehlentscheidungen nach sich ziehen könnten.

Die Abweichungen zwischen xs:double und xs:decimal werden deutlich bei einem einfachen Vergleich, in dem zwei Schleifen ineinanderlaufen und die Werte in Variablen unterschiedlichen Typs casten.

Der hier erzwungene Type-Cast dient auch zur Verdeutlichung, dass der Prozessor-interne Type-Cast dem Programmierer möglicherweise nicht bewusst ist. Wenn der Type-Cast für xs:double für vzdouble und vsdouble entfernt wird, ergibt sich kein Fehler, im Gegensatz zu der vorher dargestellten Logik.


<ergebnis>
 <xsl:for-each select="1 to 10">
  <xsl:variable name="vzdouble" 
       select=". div 10" as="xs:double" />
  <xsl:variable name="vzdecimal" 
       select="(xs:decimal(.) div 10)" as="xs:decimal" />
  <xsl:for-each select="1 to 10">
   <xsl:variable name="vsdouble" 
        select="." as="xs:double" />
   <xsl:variable name="vsdecimal" 
        select="xs:decimal(.)" as="xs:decimal" />
   <xsl:if test="($vzdouble * $vsdouble) != ($vzdecimal * $vsdecimal)">
    <Differenz z="{$vzdouble}" s="{$vsdouble}" 
     ergdouble="{$vzdouble * $vsdouble}"
     ergdecimal="{$vzdecimal * $vsdecimal}" />
   </xsl:if>
  </xsl:for-each>
 </xsl:for-each>
</ergebnis>

Dieser vergleichsweise sehr bescheidene Ansatz von lediglich 10 x 10 = 100 Wertekombinationen ergibt immerhin 21 Abweichungen. Das bedeutet eine Abweichungs-Quote von gut zwanzig Prozent. Für mich ist das ein hinreichender Grund, sich etwas näher mit dem Type-Casting und also mit XSLT 2.0 zu befassen.


  <ergebnis>
   <Differenz z="0.1" s="3" 
           ergdouble="0.30000000000000004" 
           ergdecimal="0.3"/>
   <Differenz z="0.1" s="6" 
           ergdouble="0.6000000000000001" 
           ergdecimal="0.6"/>
   <Differenz z="0.1" s="7" 
           ergdouble="0.7000000000000001" 
           ergdecimal="0.7"/>
   <Differenz z="0.2" s="3" 
           ergdouble="0.6000000000000001" 
           ergdecimal="0.6"/>
   <Differenz z="0.2" s="6" 
           ergdouble="1.2000000000000002" 
           ergdecimal="1.2"/>
   <Differenz z="0.2" s="7" 
           ergdouble="1.4000000000000001" 
           ergdecimal="1.4"/>
   <Differenz z="0.3" s="3" 
           ergdouble="0.8999999999999999" 
           ergdecimal="0.9"/>
   <Differenz z="0.3" s="6" 
           ergdouble="1.7999999999999998" 
           ergdecimal="1.8"/>
   <Differenz z="0.3" s="9" 
           ergdouble="2.6999999999999997" 
           ergdecimal="2.7"/>
   <Differenz z="0.4" s="3" 
           ergdouble="1.2000000000000002" 
           ergdecimal="1.2"/>
   <Differenz z="0.4" s="6" 
           ergdouble="2.4000000000000004" 
           ergdecimal="2.4"/>
   <Differenz z="0.4" s="7" 
           ergdouble="2.8000000000000003" 
           ergdecimal="2.8"/>
   <Differenz z="0.6" s="3" 
           ergdouble="1.7999999999999998" 
           ergdecimal="1.8"/>
   <Differenz z="0.6" s="6" 
           ergdouble="3.5999999999999996" 
           ergdecimal="3.6"/>
   <Differenz z="0.6" s="9" 
           ergdouble="5.3999999999999995" 
           ergdecimal="5.4"/>
   <Differenz z="0.7" s="3" 
           ergdouble="2.0999999999999996" 
           ergdecimal="2.1"/>
   <Differenz z="0.7" s="6" 
           ergdouble="4.199999999999999" 
           ergdecimal="4.2"/>
   <Differenz z="0.7" s="7" 
           ergdouble="4.8999999999999995" 
           ergdecimal="4.9"/>
   <Differenz z="0.8" s="3" 
           ergdouble="2.4000000000000004" 
           ergdecimal="2.4"/>
   <Differenz z="0.8" s="6" 
           ergdouble="4.800000000000001" 
           ergdecimal="4.8"/>
   <Differenz z="0.8" s="7" 
           ergdouble="5.6000000000000005" 
           ergdecimal="5.6"/>
  </ergebnis>

Boolesche Operatoren

Für die typsichere Arbeit mit Boolean-Werten empfiehlt sich die Verwendung des XML-Schema-Datentyps xs:boolean.

boolean(obj) Wandelt Zahlen nach true, Null nach false. Bei Verwendung von XML-Schema-Typen in XSLT 2.0/XPath 2.0 empfiehlt sich die Arbeit mit xs:boolean.
false() Gibt false (0) zurück.
true() Gibt true (1) zurück.
not(boolean) Gibt true -->false und false --> true.

Im folgenden Beispiel ergeben die Elemente t1 bis t6 jeweils true bzw. WAHR; t7 bis t9 sind false, also ohne Ergebnis.


 <info>
  <t1><xsl:value-of select="xs:boolean(true())"/></t1> 
  <t2><xsl:value-of select="xs:boolean('true')"/></t2> 
  <t3><xsl:value-of select="xs:boolean(1)"/></t3> 
  <t4><xsl:if test="xs:boolean(1)">WAHR</xsl:if></t4>  
  <t5><xsl:if test="true()">WAHR</xsl:if></t5>
  <t6><xsl:if test="1">WAHR</xsl:if></t6>  
  <t7><xsl:if test="false()">FALSCH</xsl:if></t7>
  <t8><xsl:if test="0">FALSCH</xsl:if></t8>
  <t9><xsl:if test="xs:boolean('false')">FALSCH</xsl:if></t9>   
 </info>

Ergebnisse:


 <info>
  <t1>true</t1>
  <t2>true</t2>
  <t3>true</t3>
  <t4>WAHR</t4>
  <t5>WAHR</t5>
  <t6>WAHR</t6>
  <t7 />
  <t8 />
  <t9 />
 </info>

wg / 12. 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/XPath_Operatoren.html