Группировка узлов xml по значению дочернего элемента в Xsl
<root>
<element>
<id>1</id>
<group>first</group>
</element>
<element>
<id>2</id>
<group>second</group>
</element>
<element>
<id>3</id>
<group>first</group>
</element>
...
<root>
Как я могу сгруппировать свои элементы по имени группы в xslt 1.0.
вывод:
<root>
<group name="first">
<element>
<id>1</id>
<group>first</group>
</element>
<element>
<id>3</id>
<group>first</group>
</element>
</group>
<group name="second">
<element>
<id>2</id>
<group>second</group>
</element>
</group>
</root>
Любые идеи?
Ответы
Ответ 1
Это работа для Muenchian Grouping. Вы найдете многочисленные примеры этого в теге XSLT здесь, в StackOverflow.
Сначала вам нужно определить ключ, который поможет вам группировать группы
<xsl:key name="groups" match="group" use="."/>
Это вызовет элементы group для заданного имени группы.
Далее вам нужно сопоставить все вхождения первого экземпляра каждого имени группы distince. Это делается с помощью этого страшного заявления
<xsl:apply-templates select="element/group[generate-id() = generate-id(key('groups', .)[1])]"/>
i.e Сопоставьте элементы группы, которые являются первым вступлением этого элемента в наш ключ.
Когда вы сопоставляете отдельные узлы группы, вы можете прокручивать все остальные узлы группы с тем же именем (где $currentGroup - это переменная, содержащая текущее имя группы)
<xsl:for-each select="key('groups', $currentGroup)">
Вводя это, дает
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="groups" match="group" use="."/>
<xsl:template match="/root">
<root>
<xsl:apply-templates select="element/group[generate-id() = generate-id(key('groups', .)[1])]"/>
</root>
</xsl:template>
<xsl:template match="group">
<xsl:variable name="currentGroup" select="."/>
<group>
<xsl:attribute name="name">
<xsl:value-of select="$currentGroup"/>
</xsl:attribute>
<xsl:for-each select="key('groups', $currentGroup)">
<element>
<id>
<xsl:value-of select="../id"/>
</id>
<name>
<xsl:value-of select="$currentGroup"/>
</name>
</element>
</xsl:for-each>
</group>
</xsl:template>
</xsl:stylesheet>
Применение этого примера на XML-примере дает следующий результат
<root>
<group name="first">
<element>
<id>1</id>
<name>first</name>
</element>
<element>
<id>3</id>
<name>first</name>
</element>
</group>
<group name="seccond">
<element>
<id>2</id>
<name>seccond</name>
</element>
</group>
</root>
Ответ 2
I. Вот полное и очень короткое решение XSLT 1.0:
<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="kElsByGroup" match="element" use="group"/>
<xsl:template match="/*">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match=
"element[generate-id()=generate-id(key('kElsByGroup',group)[1])]">
<group name="{group}">
<xsl:copy-of select="key('kElsByGroup',group)"/>
</group>
</xsl:template>
<xsl:template match=
"element[not(generate-id()=generate-id(key('kElsByGroup',group)[1]))]"/>
</xsl:stylesheet>
, когда это преобразование применяется к предоставленному XML-документу:
<root>
<element>
<id>1</id>
<group>first</group>
</element>
<element>
<id>2</id>
<group>second</group>
</element>
<element>
<id>3</id>
<group>first</group>
</element>
</root>
требуется, правильный результат получается:
<root>
<group name="first"><element>
<id>1</id>
<group>first</group>
</element><element>
<id>3</id>
<group>first</group>
</element></group>
<group name="second"><element>
<id>2</id>
<group>second</group>
</element></group>
</root>
Обратите внимание:
-
Использование Muenchian метод группировки. Это самый эффективный метод группировки в XSLT 1.0.
-
Использование AVT (Шаблон значения атрибута), чтобы указать литеральный элемент результата и его атрибут variable-value как одно целое. Использование AVT упрощает кодирование и дает более короткий и понятный код.
II. Еще более короткое решение XSLT 2.0:
<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="/*">
<root>
<xsl:for-each-group select="element" group-by="group">
<group name="{current-grouping-key()}">
<xsl:copy-of select="current-group()"/>
</group>
</xsl:for-each-group>
</root>
</xsl:template>
</xsl:stylesheet>
, когда это преобразование применяется к одному и тому же документу XML (выше), снова создается тот же правильный результат.
Обратите внимание:
0,1. Использование инструкции <xsl:for-each-group>
XSLT 2.0.
0,2. Использование стандартных функций XSLT 2.0 current-group()
и current-grouping-key()
Ответ 3
<xsl:template match="group[not(.=preceding::group)]">
<xsl:variable name="current-group" select="." />
<xsl:for-each select="//root/element[group=$current-group]">
<group>
<id><xsl:value-of select="id"/></id>
</group>
</xsl:for-each>
</xsl:template>