Стандартный подход к решению такого рода задач в XSLT 1.0 называется группировка по Мюнху. Вы определяете ключ, который группирует ваши целевые элементы так, как вы хотите.
<xsl:key name="bsById" match="b" use="@id" />
затем используйте трюк с generate-id
, чтобы извлечь только первый узел в каждой группе в качестве прокси для группы в целом
<xsl:apply-templates select="b[generate-id()
= generate-id(key('bsById', @id)[1])]"
mode="group">
<xsl:sort select="@id" />
</xsl:apply-templates>
Итак, теперь следующий шаблон будет запускаться один раз для каждой группы, и вы можете использовать внутри него функцию key
, чтобы получить все узлы в группе.
<xsl:template match="b" mode="group">
<table>
<!-- extract all the nodes that are grouped with this one -->
<xsl:apply-templates select="key('bsById', @id)">
<!-- you could <xsl:sort> here if you want to sort within groups -->
</xsl:apply-templates>
</table>
</xsl:template>
<xsl:template match="b">
<tr><td>...</td></tr>
</xsl:template>
Все вышеперечисленное хорошо, если этот пример представляет собой весь ваш XML-документ, но если в документе есть более одного элемента a
, каждый из которых имеет собственный набор элементов b
, которые необходимо сгруппировать независимо, тогда ключ должен быть более сложным. Обычный трюк здесь состоит в том, чтобы использовать generate-id
родительского узла a
как часть значения ключа группировки для его b
дочерних элементов:
<xsl:key name="bsByParentAndId" match="a/b" use="concat(generate-id(..), '|', @id)" />
и для выражения мюнхианской группировки
<xsl:template match="a">
<xsl:apply-templates select="b[generate-id()
= generate-id(key('bsByParentAndId', concat(
generate-id(current()), '|', @id))[1])]"
mode="group"/>
</xsl:template>
Кстати, если бы вы могли использовать XSLT 2.0, это стало бы значительно проще. Нет необходимости определять сложный ключ, вы просто используете for-each-group
<xsl:template match="a">
<xsl:for-each-group select="b" group-by="@id">
<xsl:sort select="current-grouping-key()" />
<table>
<xsl:apply-templates select="current-group()" />
</table>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="b">
<tr><td>...</td></tr>
</xsl:template>
person
Ian Roberts
schedule
08.08.2013