XML | XML-Schema | XPath | XSL-T | XSL-FO | XQuery | XProc | SVG |
XPath / XPath-Achsen / 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.
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>
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>
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.
V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn
☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de