В текущем принятом ответе есть один недостаток : при наличии атрибута data
с тем же именем, что и у дочернего элемента data
, сортировка всегда будет выполняться с использованием в качестве ключей значений атрибута. Кроме того, это слишком долго.
Это решение решает проблему (и короче), позволяя указать, должна ли сортировка выполняться по имени атрибута или по имени элемента:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="sortby" select="'attrib!name'"/>
<xsl:param name="orderas" select="'ascending'"/>
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="root">
<root>
<xsl:apply-templates select="data">
<xsl:sort select=
"*[name()=substring-after($sortby, 'elem!')]
|
@*[name()=substring-after($sortby, 'attrib!')]"
data-type="text" order="{$orderas}"/>
</xsl:apply-templates>
</root>
</xsl:template>
<xsl:template match="data">
<data ctrlname="{@name}" value="{value}"
comment="{comment}"/>
</xsl:template>
</xsl:stylesheet>
При применении к этому документу XML (на основе предоставленного, но сделанного немного более интересным):
<root>
<data name="Test3.Text" xml:space="preserve">
<value>Please Pick Bare Pump</value>
<comment>Tab - Pump Configuration</comment>
<name>X</name>
</data>
<data name="Test2.Text" xml:space="preserve">
<value>Complete</value>
<comment>A07</comment>
<name>Z</name>
</data>
<data name="Test1.Text" xml:space="preserve">
<value>Confirmed</value>
<comment>A01</comment>
<name>Y</name>
</data>
</root>
получен правильный результат (отсортированный по атрибуту name
) :
<root>
<data ctrlname="Test1.Text" value="Confirmed" comment="A01"/>
<data ctrlname="Test2.Text" value="Complete" comment="A07"/>
<data ctrlname="Test3.Text" value="Please Pick Bare Pump" comment="Tab - Pump Configuration"/>
</root>
Теперь замените <xsl:param name="sortby" select="'attrib!name'"/>
на :
<xsl:param name="sortby" select="'elem!name'"/>
и примените преобразование снова к тому же XML-документу. На этот раз мы получим результат, правильно отсортированный по значениям элемента child- name
:
<root>
<data ctrlname="Test3.Text" value="Please Pick Bare Pump" comment="Tab - Pump Configuration"/>
<data ctrlname="Test1.Text" value="Confirmed" comment="A01"/>
<data ctrlname="Test2.Text" value="Complete" comment="A07"/>
</root>
Объяснение
Чтобы различить, хотим ли мы сортировать по элементу-потомку или по атрибуту, мы используем соглашение, согласно которому elem!someName
означает, что сортировка должна выполняться по значениям дочернего элемента с именем someName
. Аналогично, attrib!someName
означает, что сортировка должна выполняться по значениям атрибута с именем someName
.
Надпись <xsl:sort>
изменяется соответствующим образом для правильного выбора в качестве ключа либо атрибута, либо дочернего элемента. Не допускается двусмысленность, поскольку начальная подстрока параметра sortby
теперь однозначно определяет, должен ли ключ быть атрибутом или дочерним элементом.