Это можно легко решить с помощью XPath. Вот выражение, которое вы ищете: count((.|preceding-sibling::ROLE)[not(@name = preceding-sibling::ROLE/@name)])
Это можно разбить, чтобы сделать его более читабельным, как я сделал в следующей таблице стилей XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<!-- don't copy whitespace -->
<xsl:template match="text()"/>
<xsl:template match="ROLE">
<xsl:variable name="roles-so-far" select=". | preceding-sibling::ROLE"/>
<!-- Only select the first instance of each ROLE name -->
<xsl:variable name="roles-so-far-unique"
select="$roles-so-far[not(@name = preceding-sibling::ROLE/@name)]"/>
<xsl:apply-templates select="@name"/>
<xsl:text> </xsl:text>
<xsl:value-of select="count($roles-so-far-unique)"/>
<xsl:text>
</xsl:text> <!-- linefeed -->
</xsl:template>
</xsl:stylesheet>
Вот альтернативная реализация, использующая метод Мюнхена. Сначала объявите ключ:
<xsl:key name="roles" match="ROLE" use="@name"/>
Затем замените определение $ роли, пока что уникальное на что-то вроде этого:
<!-- Among all the ROLEs having one of the names so far,
select only the first one for each name -->
<xsl:variable name="roles-so-far-unique"
select="../ROLE[@name = $roles-so-far/@name]
[generate-id(.) = generate-id(key('roles',@name)[1])]"/>
Этот код, конечно, более сложный. Если у вас нет большого набора данных, требующего ускорения обработки с использованием метода Мюнхена (даже тогда я бы протестировал, чтобы убедиться, что он что-то покупает), вы могли бы также придерживаться более простой версии выше.
Наконец, в XSLT 2.0 это намного проще. Простое замените уникальное определение $ role следующим:
<!-- Return a list of distinct string values, with duplicates removed -->
<xsl:variable name="roles-so-far-unique"
select="distinct-values($roles-so-far/@name)"/>
Надеюсь, это помогло вам определить, где вы ошиблись в различных упомянутых вами попытках.