Чтобы получить первое и последнее вхождение (порядок документов) в каждой группе "<val>
", вы можете использовать <xsl:key>
, например:
<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>
РЕДАКТИРОВАТЬ # 1: Небольшое объяснение, поскольку это может быть неочевидно сразу:
-
<xsl:key>
возвращает все <container>
узлы, имеющие данный <val>
. Вы используете функцию key()
, чтобы запросить ее.
-
<xsl:variable>
- это то, где все это происходит. Это читается как:
- для каждого из
<container>
узлов в документе ("//container
") проверьте ...
- … если он имеет тот же уникальный идентификатор (
generate-id()
), что и первый узел, возвращаемый key()
, или последний узел, возвращаемый key()
- , где
key('ContainerGroupByVal', val)
возвращает набор <container>
узлов, соответствующих текущему <val>
- , если уникальные идентификаторы совпадают, включить узел в выборку
-
<xsl:for-each>
выводит. С таким же успехом это может быть <xsl:apply-templates>
.
РЕДАКТИРОВАТЬ # 2: Как справедливо отмечает Димитр Новатчев в комментариях, вы должны быть осторожны с использованием "//
" сокращения XPath. Если вы можете избежать этого, во что бы то ни стало, сделайте это & mdash; частично потому, что он потенциально выбирает узлы, которые вам не нужны, и главным образом потому, что он медленнее, чем более конкретное выражение XPath. Например, если ваш документ выглядит так:
<containers>
<container><!-- ... --></container>
<container><!-- ... --></container>
<container><!-- ... --></container>
</containers>
тогда вы должны использовать "/containers/container
" или "/*/container
" вместо "//container
".
РЕДАКТИРОВАТЬ # 3: Альтернативный синтаксис вышеупомянутого будет:
<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>
Объяснение: Оператор объединения XPath "|
" объединяет свои аргументы в набор узлов. По определению набор узлов не может содержать дубликаты узлов & mdash; например: «. | . | .
» создаст набор узлов, содержащий ровно один узел (текущий узел).
Это означает, что если мы создадим набор узлов объединения из текущего узла ("."), Узла "key(…)[1]
" и узла "key(…)[last()]
", его число будет равно 2, если (и только если) текущий узел равен одному из двух других узлов, во всех остальных случаях количество будет равно 3.