Home
Über mich
Veröffentlichungen

XML XML-Schema XPath XSL-T XSL-FO XQuery XProc SVG

XPath / XPath-Achsen / Positionsbestimmung bei following-sibling

Positionsbestimmung bei following-sibling

Positionsbestimmung bei following-sibling

➪ Mitunter stehen Sie vor der Spezialaufgabe, mit following-sibling::* die Abstände zwischen Elementen zu ermitteln, die sich beispielsweise durch das Vorhandensein eines bestimmten Attributs unterscheiden.

Auf dieser Seite:

Auch hier soll ein Beispiel dienen. Gegeben sei das folgende XML-Input-Dokument:


<root>
    <info>1</info>
    <info>2</info>
    <info>3</info>
    <info>4</info>
    <info>5</info>
    <info>6</info>
    <info irgendeinattribut="">7</info>
    <info>8</info>
    <info>9</info>
    <info>10</info>
    <info>11</info>
    <info irgendeinattribut="">12</info>
    <info>13</info>
    <info>14</info>
    <info>15</info>
    <info>16</info>
    <info>17</info>
    <info>18</info>
    <info irgendeinattribut="">19</info>
    <info>20</info>
</root>

Die Aufgabe ist, bei allen Elementen, die das Attribut irgendeinattribut aufweisen, den Abstand zum jeweils nächstfolgenden Element (following-sibling) zu ermitteln, das dieses Attribut ebenfalls aufweist.

Lösung in XQuery


for $w at $p in /root/info return 
  if ( $w/@irgendeinattribut) 
  then (
    element {name($w)}{
      attribute {'irgendeinattribut'}{},
      attribute {'Abstand'}{
        min(
          for $x at $y in $w/following-sibling::info
          where $x/@irgendeinattribut 
          return $y
        )
      }
    } 
  ) 
  else ($w)

Das Ergebnis sieht so aus:


<root>
   <info nr="1"></info>
   <info nr="2"></info>
   <info nr="3"></info>
   <info nr="4"></info>
   <info nr="5"></info>
   <info nr="6"></info>
   <info nr="7" irgendeinattribut="" 
                Abstand="5"></info>
   <info nr="8"></info>
   <info nr="9"></info>
   <info nr="10"></info>
   <info nr="11"></info>
   <info nr="12" irgendeinattribut="" 
                 Abstand="7"></info>
   <info nr="13"></info>
   <info nr="14"></info>
   <info nr="15"></info>
   <info nr="16"></info>
   <info nr="17"></info>
   <info nr="18"></info>
   <info nr="19" irgendeinattribut=""></info>
   <info nr="20"></info>
</root>

Lösung in XSLT 1.0 über eine separate Variable

Zunächst geht es darum, die Position der gesuchten info-Elemente in einer separaten Variablen festzuhalten. Das geschieht über die position()-Funktion, die eine Nummerierung ermöglicht.


<xsl:variable name="vwithpos">
    <xsl:for-each select="//root/child::*">
        <xsl:if test="@irgendeinattribut">
            <info pos="{position()}"/>                
        </xsl:if>
    </xsl:for-each>
</xsl:variable>

Im nächsten Schritt können Sie die aktuelle Position mit jener in vwithpos abgleichen und Differenzen zum nächsten $vwithpos/info/@pos (following-sibling an der position()V=1) berechnen.


<xsl:template match="info" mode="folgendeElemente">
  <xsl:variable name="vpos" select="position()"/>
  <info>
    <xsl:for-each select="attribute()">
      <xsl:copy-of select="."/>
    </xsl:for-each>
    <xsl:if test="self::info[@irgendeinattribut]">
      <xsl:value-of 
           select="$vwithpos/info[@pos = $vpos]
                   /following-sibling::info[position()=1]
                   /@pos - $vpos"/>       
    </xsl:if>
  </info>
</xsl:template>

Lösung in XSLT 2.0

Alternativ können Sie in XSLT 2.0 jene Positionen durch die Differenz zum Minimum des jeweils folgenden info-Elements (wiederum mit following-sibling) ermitteln, das das Attribut "irgendeinattribut" aufweist.


<xsl:template match="/">
  <root>   
   <xsl:for-each select="/root/info">
    <xsl:variable name="vpos" select="position()"/>
    <info nr="{$vpos}">
     <xsl:for-each select="attribute()">
      <xsl:copy-of select="."/>
     </xsl:for-each>     
     <xsl:if test="self::info[@irgendeinattribut] and 
                   following-sibling::info[@irgendeinattribut]">
     <xsl:attribute name="Abstand">
      <xsl:value-of select="min( 
       for $x in following-sibling::info return 
         if ($x/@irgendeinattribut) 
         then ( count($x/preceding-sibling::info) + 1 ) 
         else nothing
       ) - $vpos"/>
     </xsl:attribute>
     </xsl:if>
    </info>
   </xsl:for-each>
  </root>
</xsl:template>

wg / 10. Januar 2020



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/followingsibling.html