Другой немного другой подход (хотя и не обязательно лучший) заключается в передаче параметра каждому совпадению student , которое содержит разделенные запятыми атрибуты group , которые уже были выведены,Каждый раз, когда вы сопоставляете студента , вы проверяете, входит ли его группа в его параметр, и если не выводится студент, и получаете следующую группу, добавляя текущую группу к параметру.
Вот XSLT, который я прокомментировал, чтобы попытаться объяснить вещи лучше
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="Socrates">
<Socrates>
<xsl:apply-templates select="Student[1]"/>
</Socrates>
</xsl:template>
<xsl:template match="Student">
<!-- Parameter containin comma-delimited list of currently output groups -->
<xsl:param name="groupList" select="','" />
<xsl:choose>
<!-- Has the group already been output? -->
<xsl:when test="contains($groupList, concat(',', @group, ','))">
<!-- If so, move on to next student record -->
<xsl:apply-templates select="following-sibling::Student[1]">
<xsl:with-param name="groupList" select="$groupList" />
</xsl:apply-templates>
</xsl:when>
<!-- Group has not already been output -->
<xsl:otherwise>
<!-- Output the record -->
<xsl:copy-of select="." />
<!-- Get the next student with a different name -->
<xsl:apply-templates select="following-sibling::Student[@name!=current()/@name][1]">
<xsl:with-param name="groupList" select="concat($groupList, @group, ',')" />
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
При применении к вашему образцу XML выдается следующее:
<Socrates>
<Student name="Aristotle" group="1" />
<Student name="Plato" group="2" />
<Student name="Xenophon" group="4" />
<Student name="Critias" group="3" />
</Socrates>
Обратите внимание, что это предполагает student элементы всегда упорядочены по имени во входном XML.