Вы получили хорошие ответы уже. В погоне за краткости, я представляю свое решение 16 линии, на основе Dimitres answer:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<choices>
<xsl:for-each-group select="option" group-by="title">
<choice>
<xsl:sequence select="title"/>
<xsl:for-each-group select="current-group()/desc" group-by=".">
<xsl:sequence select="."/>
</xsl:for-each-group>
</choice>
</xsl:for-each-group>
</choices>
</xsl:template>
</xsl:stylesheet>
Обратите внимание, что текущий узел контекста внутри for-each-group
является первым элементом в текущей группе, в то время как current-group()
возвращает список всех элементов в текущей группы. Я использую тот факт, что элемент title
идентичен для ввода и вывода и копирует первый заголовок из каждой группы.
И для полноты, то XSLT 1.0 решение с использованием Muenchian группировки (20 линий):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="title" match="option/title" use="."/>
<xsl:key name="desc" match="option/desc" use="."/>
<xsl:template match="/*">
<choices>
<xsl:for-each select="option/title[count(.|key('title',.)[1]) = 1]">
<choice>
<xsl:copy-of select="."/>
<xsl:for-each select="key('title',.)/../desc
[count(.|key('desc', .)[../title=current()][1]) = 1]">
<xsl:copy-of select="."/>
</xsl:for-each>
</choice>
</xsl:for-each>
</choices>
</xsl:template>
</xsl:stylesheet>
Хороший вопрос (+1). См. Мой ответ на минимальное (19 строк) решение XSLT 2.0. :) –