#xslt #sorting #xslt-2.0
#xslt #сортировка #xslt-2.0
Вопрос:
Я пытаюсь найти решение следующей проблемы.
Я разрабатываю преобразование XSLT (которое сейчас имеет размер около 40 КБ), которое преобразует довольно сложные XML-файлы в довольно простую структуру, которая хотела бы этого:
<Records>
<Record key="XX">
</Record>
<Record key="XX1">
</Record>
<Record key="XX2">
</Record>
<Record key="XX3">
</Record>
</Records>
Я хотел бы, чтобы этот выходной XML был отсортирован по Records/Record/@key
значениям.
Проблема в том, что мой XSLT выдает этот вывод без сортировки, и из-за его сложности я не могу отсортировать его там.
Можно ли применить xsl:sort
к выходному XML? Я знаю, что могу подготовить другое преобразование XSLT, но в моем случае это не решение, поскольку я ограничен только одним XSLT .. Пожалуйста, помогите !…
Ответ №1:
Можно ли применить xsl:sort к выходному XML?
Да, многопроходная обработка возможна, и особенно в XSLT 2.0 вам даже не нужно применять xxx:node-set()
расширение к результату, потому что печально известный тип RTF больше не существует:
<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="/">
<xsl:variable name="vPass1">
<!--
Put/Invoke your cirrent code here
to generate the following
-->
<Records>
<Record key="XX3">
</Record>
<Record key="XX2">
</Record>
<Record key="XX4">
</Record>
<Record key="XX1">
</Record>
</Records>
</xsl:variable>
<xsl:apply-templates select="$vPass1/*"/>
</xsl:template>
<xsl:template match="Records">
<Records>
<xsl:perform-sort select="*">
<xsl:sort select="@key"/>
</xsl:perform-sort>
</Records>
</xsl:template>
</xsl:stylesheet>
Когда это преобразование выполняется для любого XML-документа (не используется / игнорируется), создается требуемый, правильный, отсортированный результат:
<Records>
<Record key="XX1"/>
<Record key="XX2"/>
<Record key="XX3"/>
<Record key="XX4"/>
</Records>
В XSLT 1.0 это почти то же самое с дополнительным преобразованием результата из RTF-типа в обычное дерево:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<!--
Put/Invoke your cirrent code here
to generate the following
-->
<Records>
<Record key="XX3">
</Record>
<Record key="XX2">
</Record>
<Record key="XX4">
</Record>
<Record key="XX1">
</Record>
</Records>
</xsl:variable>
<xsl:variable name="vPass1"
select="ext:node-set($vrtfPass1)"/>
<xsl:apply-templates select="$vPass1/*"/>
</xsl:template>
<xsl:template match="Records">
<Records>
<xsl:for-each select="*">
<xsl:sort select="@key"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</Records>
</xsl:template>
</xsl:stylesheet>
Комментарии:
1. большое спасибо за ответ.. Я пытался выполнить первый пример кода, но, похоже, он не работает. Это ничего не дает… . В общем, я понял идею. Я буду продолжать пытаться разобраться в этом самостоятельно.
2. @LukaszBaran: Как вы думаете, как я создал этот код? Я всегда тестирую свой код перед его публикацией. На самом деле, то, что вы видите как «результат», всегда копируется и вставляется из фактического результата в моем тесте. Если у вас возникли какие-либо проблемы при выполнении первого решения, это, скорее всего, означает, что вы не запускали его с процессором XSLT 2.0. Найдите себе один и попробуйте еще раз. Также возможно, что вы заменили комментарий своим кодом, и в вашем коде есть шаблоны, соответствующие тем же узлам (например, document-node() ).). Всегда безопаснее поместить весь код для данного прохода в именованный режим, например «mPass1».
3. Правильно. Я думаю, что это было вызвано Xalan, который я использую для преобразования XSLT.
4. @LukaszBaran: Итак, в этом случае вы должны использовать решение XSLT 1.0. Если EXSLT недоступен, используйте встроенную функцию расширения Xalan
xxx:nodeset()
5. Хорошо, это работает! Я применил последнюю версию Saxon. Большое спасибо, 1, и я принимаю это решение.
Ответ №2:
40 КБ — это много кода для одной таблицы стилей. Когда дело доходит до такого масштаба, обычно лучше разделить преобразование на конвейер меньших преобразований. Если у вас такая конвейерная архитектура, то добавление шага сортировки в конце является тривиальным. Существует множество технологий для управления конвейером преобразований (XProc, Orbeon, xmlsh, ant, Coccoon) в зависимости от ваших требований. Преимущество конвейерной обработки заключается в том, что она сохраняет ваш код модульным и пригодным для повторного использования.
Ответ №3:
В качестве дополнения к превосходному решению Dimitre, описанному выше, если вы используете процессор XSLT 1.0 (например, .NET), следующее может дать вам указатель на то, как использовать node-set: http://www.xml.com/pub/a/2003/07/16/nodeset.html#tab.namespaces
В моем случае я был в .NET 1.1 (т. Е. MSXML), и решение выглядело примерно так:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<Records xmlns="">
<xsl:apply-templates />
</Records >
</xsl:variable>
<xsl:variable name="vPass1" select="msxsl:node-set($vrtfPass1)"/>
<xsl:apply-templates select="$vPass1/*" mode="sorting"/>
</xsl:template>
<xsl:template match="Records" mode="sorting">
<Records>
<xsl:for-each select="Record">
<xsl:sort select="@key"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</Records>
</xsl:template>
</xsl:stylesheet>