В XSLT 2.0 это довольно просто, как вы можете использовать два проход подход, первый генерировать требуемые выходы, а затем отсортировать их:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<summary>
<actions>
<xsl:perform-sort>
<xsl:sort select="@dateTime" />
<xsl:apply-templates select="summary/*/*" />
</xsl:perform-sort>
</actions>
</summary>
</xsl:template>
<xsl:template match="incoming/delivery">
<action type="incoming" dateTime="{@incDate} 00:00:00"/>
</xsl:template>
<xsl:template match="outgoing/delivery">
<action type="outgoing" dateTime="{substring(@outDate, 1, 4)}-{substring(@outDate, 5, 2)}-{substring(@outDate, 7, 2)} 23:59:59"/>
</xsl:template>
<xsl:template match="repairs/repair">
<action type="repair" dateTime="{@repairDate}"/>
</xsl:template>
</xsl:stylesheet>
Здесь мы генерирование выхода для каждого входа с использованием apply-templates
и , затем, сортируя эти сгенерированные элементы с помощью perform-sort
с использованием выражения XPath (@dateTime
) относительно сгенерированного XML, а не оригинала.
Если вы ограничены 1.0, это не вариант, поскольку вы можете сортировать только на основе входных данных XML, а не сгенерированного вывода. Таким образом, мы должны придумать 1,0 выражения одного XPath, который может обрабатывать любого из трех форматов даты и произвести подходящий ключ сортировки
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<summary>
<actions>
<xsl:apply-templates select="summary/*/*">
<xsl:sort select="
translate(
concat(
@incDate, @outDate, @repairDate,
substring('000000', 6*not(@incDate) + 1),
substring('235959', 6*not(@outDate) + 1)
),
'-: ',
''
)" />
</xsl:apply-templates>
</actions>
</summary>
</xsl:template>
<!-- the other three templates are unchanged -->
Это использует ряд трюков, прежде всего
substring('000000', 6*not(@incDate) + 1)
которая опирается на целый ряд вещей:
- аргумент
not()
трактуется как логическое значение.
- когда вы лечите узел установлен как логическое значение, пустое множество является ложным и непустым множество истинно
- литья булева номера дает 1 для истинно и 0 для ложного
поэтому эффект для возврата строки 000000
, если целевой узел имеет атрибут incDate
, а пустую строку - нет. Окончательный concat
создает строку, которая будет выглядеть как YYYY-MM-DD000000
для incDate
, YYYYMMDD235959
для outDate
и YYYY-MM-DD HH:mm:ss
для repairDate
, то translate
обрезает все пробелы, дефис и колонов, чтобы привести эти три в общий формат, который можно сравнить лексически.
Я не пробовал, потому что (если честно) У меня нет подсказки, если это возможно сделать с одним преобразованием XSLT. Я сделал это с помощью C++ и pugixml ... но я хотел попробовать что-то новое, которое не полагается на 2-й язык. – fhw72
Какую версию XSLT вы можете использовать? Это возможно только в том случае, если вы ограничены 1.0, но в 2.0 или более поздних версиях будет намного проще. –
Я могу использовать обе версии. – fhw72