Home
Über mich
Blog
Veröffentlichungen
IT-Trainings
Impressum


XPath - Operatoren

Zusammenfassung:

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

XPath - Operatoren

Numerische Operatoren

+ Addition
- Subtraktion
* Multiplikation
div Division
mod Modulo-Division: gibt den ganzzahligen Rest einer Division. Beispiel: <xsl:if test="position() mod 2 = 0">...</xsl:if> prüft, ob die aktuelle Position eines Elementes 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äßt.

<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 wir 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. OK, 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 einer 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 numerischen 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 wir 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 Prozessor 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 wir 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, daß 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 Fehl-Entscheidungen nach sich ziehen könnte.

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

Der hier erzwungene Typecast dient auch zur Verdeutlichung, daß der prozessor-interne Typecast dem Programmierer möglicherweise nicht bewußt ist, denn wenn der Typecast 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 Typecasting 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

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.

Für die typsichere Arbeit mit Boolean-Werten empfiehlt sich die Verwendung des XML Schema-Datentyps xs:boolean. 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>

qrpic/XPath_Operatoren.jpg

wg / 3. Oktober 2017




Fragen? Anmerkungen? Tips?

Bitte nehmen Sie Kontakt zu mir auf (info10@wilfried-grupe.de).



Vielen Dank für Ihr Interesse an meiner Arbeit.


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

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