Produzieren Kontextdaten zum ersten und letzten Auftreten eines jeden Wertes eines Elements
Frage
In Anbetracht der folgenden xml:
<container>
<val>2</val>
<id>1</id>
</container>
<container>
<val>2</val>
<id>2</id>
</container>
<container>
<val>2</val>
<id>3</id>
</container>
<container>
<val>4</val>
<id>1</id>
</container>
<container>
<val>4</val>
<id>2</id>
</container>
<container>
<val>4</val>
<id>3</id>
</container>
Ich möchte etwas zurückgeben wie
2 - 1
2 - 3
4 - 1
4 - 3
Mit einem nodeset konnte ich über das letzte Vorkommen erhalten:
exsl:node-set($list)/container[not(val = following::val)]
, aber ich kann nicht herausfinden, wie die ersten zu erhalten.
Lösung
Um das erste und das letzte Vorkommen (Dokumentreihenfolge) in jeder „<val>
“ Gruppe zu erhalten, können Sie eine <xsl:key>
wie folgt verwenden:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="text" />
<xsl:key name="ContainerGroupByVal" match="container" use="val" />
<xsl:variable name="ContainerGroupFirstLast" select="//container[
generate-id() = generate-id(key('ContainerGroupByVal', val)[1])
or
generate-id() = generate-id(key('ContainerGroupByVal', val)[last()])
]" />
<xsl:template match="/">
<xsl:for-each select="$ContainerGroupFirstLast">
<xsl:value-of select="val" />
<xsl:text> - </xsl:text>
<xsl:value-of select="id" />
<xsl:value-of select="' '" /><!-- LF -->
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
EDIT # 1: Ein bisschen eine Erklärung, da dies möglicherweise nicht offensichtlich sofort:
- Die
<xsl:key>
gibt alle<container>
Knoten einen bestimmten<val>
haben. Sie verwenden die Funktionkey()
es abzufragen. - Die
<xsl:variable>
ist, wo alles passiert. Es liest sich wie:- für jede der
<container>
Knoten im Dokument ( "//container
") überprüfen ... - ... wenn es die gleiche eindeutige ID (
generate-id()
) als erster Knoten durchkey()
oder den letzten Knoten vonkey()
zurückgegeben zurückgegeben - wo
key('ContainerGroupByVal', val)
den Satz von<container>
Knoten gibt den aktuellen<val>
passende - , wenn die eindeutigen IDs übereinstimmen, umfassen den Knoten in der Auswahl
- für jede der
- die
<xsl:for-each>
tut die Ausgabe. Es könnte genauso gut ein<xsl:apply-templates>
sein.
EDIT # 2: Wie Dimitre Novatchev zu Recht in den Kommentaren darauf hin, sollten Sie vorsichtig sein, um die „//
“ XPath Stenographie zu verwenden. Wenn Sie es, mit allen Mitteln vermeiden können, tun dies - zum Teil, weil es möglicherweise Knoten wählt die Sie nicht wollen, und vor allem, weil es langsamer ist als eine spezifischere XPath-Ausdruck. Zum Beispiel, wenn Sie Ihr Dokument wie folgt aussieht:
<containers>
<container><!-- ... --></container>
<container><!-- ... --></container>
<container><!-- ... --></container>
</containers>
, dann sollten Sie "/containers/container
" oder "/*/container
" anstelle von "//container
" verwenden.
EDIT # 3: Eine alternative Syntax der oben wäre:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="text" />
<xsl:key name="ContainerGroupByVal" match="container" use="val" />
<xsl:variable name="ContainerGroupFirstLast" select="//container[
count(
.
| key('ContainerGroupByVal', val)[1]
| key('ContainerGroupByVal', val)[last()]
) = 2
]" />
<xsl:template match="/">
<xsl:for-each select="$ContainerGroupFirstLast">
<xsl:value-of select="val" />
<xsl:text> - </xsl:text>
<xsl:value-of select="id" />
<xsl:value-of select="' '" /><!-- LF -->
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Erläuterung: Die XPath-Operator union „|
“ verbindet es Argumente in eine Knotenmenge ist. Per Definition kann ein Knoten-Set enthält keine doppelten Knoten - zum Beispiel: „. | . | .
“ wird ein Knoten-Set erstellen enthält genau einen Knoten (der aktuelle Knoten)
Das heißt, wenn wir eine Union Knotenmenge aus dem aktuellen Knoten ( „“), der ‚key(…)[1]
‘ Knoten und die ‚key(…)[last()]
‘ Knoten erstellen, es Knotenzahl sind wird 2, wenn (und nur dann) die aktuelle Knoten ist gleich einer der beiden anderen Knoten, in allen anderen Fällen die Zählung 3 sein.
Andere Tipps
XPath :
//container[position() = 1] <- this is the first one
//container[position() = last()] <- this is the last one
Hier ist eine Reihe von XPath-Funktionen im Detail.
ich. XSLT 1.0
Im Grunde der gleiche Lösung wie die von Tomalak, aber verständlichen Auch ist es vollständig, so dass Sie nur das XML-Dokument und die Transformation kopieren und einfügen müssen und dann drücken Sie einfach die "Transform" Button Ihres Lieblings XSLT IDE :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kContByVal" match="container"
use="val"/>
<xsl:template match="/*">
<xsl:for-each select=
"container[generate-id()
=
generate-id(key('kContByVal',val)[1])
]
">
<xsl:variable name="vthisvalGroup"
select="key('kContByVal', val)"/>
<xsl:value-of select=
"concat($vthisvalGroup[1]/val,
'-',
$vthisvalGroup[1]/id,
'
'
)
"/>
<xsl:value-of select=
"concat($vthisvalGroup[last()]/val,
'-',
$vthisvalGroup[last()]/id,
'
'
)
"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
, wenn diese Transformation auf dem ursprünglich bereitgestellten XML-Dokument (bearbeitet werden wohlgeformt) angewandt wird:
<t>
<container>
<val>2</val>
<id>1</id>
</container>
<container>
<val>2</val>
<id>2</id>
</container>
<container>
<val>2</val>
<id>3</id>
</container>
<container>
<val>4</val>
<id>1</id>
</container>
<container>
<val>4</val>
<id>2</id>
</container>
<container>
<val>4</val>
<id>3</id>
</container>
</t>
das gewünschte Ergebnis erzeugt :
2-1
2-3
4-1
4-3
Hinweis: :
- Wir verwenden die Muench-Methode für die Gruppierung für jeden Satz solcher Elemente ein
container
Element zu finden, die denselben Wert fürval
haben. - Aus der ganzen Knoten-Liste der
container
Elemente mit dem gleichenval
Wert , wir gebe die erforderlichen Daten für das erstecontainer
Element in der Gruppe und für das letztecontainer
Element in der Gruppe.
II. XSLT 2.0
Diese Transformation :
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:for-each-group select="container"
group-by="val">
<xsl:for-each select="current-group()[1], current-group()[last()]">
<xsl:value-of select=
"concat(val, '-', id, '
')"/>
</xsl:for-each>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
, wenn auf dem gleichen XML-Dokument wie oben angewandt, prodices das gewünschte Ergebnis :
2-1
2-3
4-1
4-3
Hinweis: :
-
Die Verwendung der
<xsl:for-each-group>
XSLT-Anweisung. -
Die Verwendung der
current-group()
Funktion.