Одним из способов было бы протолкнуть элементы через разные шаблоны, где вы используете ключи для определения групп и условий, которые у вас есть для групп, а затем сформировать группу для первого элемента в каждой группе и использовать пустой шаблон для другой предметы в каждой группе; обратите внимание, что следующее использует приоритет, наложенный порядком https://www.w3.org/TR/xslt-30/#conflict, поэтому порядок, используемый ниже для шаблонов, важен, хотя вы также можете использовать атрибуты priority
, чтобы навязывать свои правила в пользу группировки на основе @id
, тогда Группировка на основе @name
, затем группируйте любой элемент (например, match="item"
), на который не распространяются другие условия группировки:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="id" match="item[normalize-space(@id)]" use="@id"/>
<xsl:key name="name" match="item" use="@name"/>
<xsl:template match="item">
<group>
<xsl:copy-of select="."/>
</group>
</xsl:template>
<xsl:template match="item[key('name', @name)[2] and . is key('name', @name)[1]]">
<group>
<xsl:copy-of select="key('name', @name)"/>
</group>
</xsl:template>
<xsl:template match="item[key('name', @name)[2] and not(. is key('name', @name)[1])]"/>
<xsl:template match="item[key('id', @id)[2] and . is key('id', @id)[1]]">
<group>
<xsl:copy-of select="key('id', @id)"/>
</group>
</xsl:template>
<xsl:template match="item[key('id', @id)[2] and not(. is key('id', @id)[1])]"/>
</xsl:stylesheet>
Онлайн на https://xsltfiddle.liberty -development.net / bdxtqt , использует XSLT 3, но вы можете заменить объявление xsl:mode
, использовавшееся в начале, шаблоном идентификации
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
чтобы он работал с процессором XSLT 2. Только предостережение о том, что процессор XSLT 2 может прибегнуть к сообщению об ошибке, если существует несколько совпадений вместо того, чтобы использовать последний соответствующий шаблон, я не помню, какой процессор XSLT 2 это делает, но, как было сказано, использование приоритетов решило бы это.
Ваш опубликованный подход также должен работать:
<xsl:template match="items">
<xsl:copy>
<xsl:variable name="nonMatched" as="element()*">
<xsl:for-each-group select="item" group-by="@id">
<xsl:sequence
select="if (not(current-group()[2]))
then .
else ()"/>
</xsl:for-each-group>
</xsl:variable>
<xsl:for-each-group select="item except $nonMatched" group-by="@id">
<group>
<xsl:apply-templates select="current-group()"/>
</group>
</xsl:for-each-group>
<xsl:for-each-group select="$nonMatched" group-by="@name">
<group>
<xsl:apply-templates select="current-group()"/>
</group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
https://xsltfiddle.liberty -development.net / 3NzcBtv